# This is a little irssi script I cooked up to use Growl for Mac OS X
# to provide pop-up notifications using Mac::Growl.

# http://www.geekfarm.org/twiki/bin/view/Main/GrowlNotify

use strict;

#  use warnings;
# 'use warnings' was generating annoying errors about global variables
# not shared in subroutines... wtf?

use vars qw($VERSION %IRSSI $APP);

use Mac::Growl;
use Irssi;
use YAML;

$VERSION = 0.05;
%IRSSI = (
          authors      =>  'VVu',
          contact      =>  'growl-notify--AT--geekfarm--DOT--org',
          name         =>  'growl notify',
          description  =>  'configurable Growl notifications on OS X for irssi.',
          license      =>  'Public Domain'
);

#############################################################################
#_* To Do

# server connect and disconnect notifications
# buddy connect/disconnect notifications
# buddy icons - waiting on the next growl release
# command to pause notification for x seconds
# docs
# messages configurable by userid
# if bbdb available, define nicks and images there

#############################################################################
#_* Config

my $configfile = "$ENV{HOME}/.irssi/growl.yaml";

my $config = {
    %{ YAML::LoadFile( $configfile ) || {} },
};

#############################################################################
#_* Global Data

# Store most recently received data to prevent duplicate paging events
my ( $lastnick, $lasttext, $laststicky );

#############################################################################
#_* Growl registration

Mac::Growl::RegisterNotifications(
                  'irssi',
                  [ 'irssi' ],
                  [ 'irssi' ],
                 );


#############################################################################
#_* Private Messages

sub sig_privmsg
{
    return unless ( $config->{"private.enable"} );
    my ($server, $data, $nick, $address) = @_;
    return undef unless ( $nick && $data );

    unless ( _supress_duplicates( $nick, $data, 1 ) )
    {
        # Specify legal characters for the popup
        $data =~ tr|[a-zA-Z0-9\!\@\#\$\%\^\&\*\(\)\_\+\-\=\[\]\{\}\;\:\'\"\,\.\<\>\/\?\`\~\ \n]||cd;

        notify (
                "$nick (private)",
                "$data",
                $config->{"private.sticky"}
               );
    }
}

Irssi::signal_add_last("message private","sig_privmsg");

#############################################################################
#_* Public Messages

sub sig_public
{
    return unless $config->{"public.enable"};
    my ( $server, $msg, $nick, $address, $target ) = @_;
    return unless ( $nick && $msg );

    # If growl is true for this channel
    my $channel = lc( $target );
    if ( $config->{"public.channels"}->{$channel} )
    {
        my $sticky;

        if ( $config->{"public.channels"}->{$channel} eq "sticky" )
        {
            $sticky = 1;
        }

        unless ( _supress_duplicates( $nick, $msg, $sticky ) )
        {
            #Irssi::print("Alerting for public message");

            notify (
                    "$nick  [$channel] (public)",
                    $msg,
                    $sticky,
                   );
        }
    }
}


Irssi::signal_add_last('message public', 'sig_public');

#############################################################################
# Hilighted Words

sub sig_printtext
{
    return unless ( $config->{"hilight.enable"} );
    my ($dest, $text, $stripped) = @_;

    if (($dest->{level} & (MSGLEVEL_HILIGHT|MSGLEVEL_MSGS)) &&
        ($dest->{level} & MSGLEVEL_NOHILIGHT) == 0) {
    
        # Pull nick and text from $stripped
        $stripped =~ m|^\<(.*?)\> (.*)$|;
        my ( $nick, $text ) = ( $1, $2 );
            return undef unless ( $nick && $text );

        # Remove any leading white space from nick to enable duplicate detection
        $nick =~ s|^\s+||;
            
        unless ( _supress_duplicates( $nick, $text, 1 ) )
        {
            notify (
                    "$nick (hilite)",
                    $text,
                    $config->{"hilight.sticky"}
                    );
        }
    }
}

Irssi::signal_add_last('print text', 'sig_printtext');

Irssi::print('%G>>%n '.$IRSSI{name}.' '.$VERSION.' loaded (/growl for help)');

#############################################################################
#_* Public Subroutines

#################
#__* notify
#
# send a pop-up notification using growl
#
sub notify
{
    my ( $subject, $text, $sticky ) = @_;

    return undef unless $subject;
    return undef unless $text;

    Mac::Growl::PostNotification(
                                 'irssi',
                                 'irssi',
                                 $subject,
                                 $text,
                                 $sticky
                                );
}

#################
#__* /growl
#
# set and query params
# save and load params to config file
#
sub growl
{
    my ( $command, @args ) = split(  '\s+', $_[0] );

    if ( $command eq 'config' )
    {
        my $param = shift @args;
        $param = "$param";
    
        unless ( defined( $config->{"$param"} ) )
        {
            Irssi::print( "ERROR: config param $param not defined" );
            return undef;
        }

        my $ref = ref $config->{$param};
    
        if ( $ref eq "HASH" )
        {
            my ( $subparam, $value ) = @args;

            if ( $value eq 'delete' )
            {
                Irssi::print( "Deleting param $param->$subparam" );
                delete $config->{$param}->{$subparam};
            } else
            {
                Irssi::print( "Setting value for $param->$subparam to $value" );
                $config->{$param}->{$subparam} = $value;
            }
        } else
        {
            my $value = shift @args;
            Irssi::print "Setting param $param to value $value";
            $config->{$param} = $value;
        }
    } elsif ( $command eq 'dump' )
    {
        my $dump = Data::Dumper->new( [ $config ] );
        Irssi::print( $dump->Dump );
    } elsif ( $command eq 'save' )
    {
        Irssi::print( "Saving config file" );
        YAML::DumpFile( $configfile, $config );
    } elsif ( $command eq 'load' )
    {
        Irssi::print( "Loading config file" );
        $config = {
                   %{ YAML::LoadFile( $configfile ) || {} },
                  };
    } else
    {
        Irssi::print( "Usage: growl <command> <arguments>" );

        Irssi::print( "\nAvailable Commands:" );
        Irssi::print( "config  <param> <value>  set simple values" );
        Irssi::print( "dump    display the current config" );
        Irssi::print( "save    save the config to the config file" );
        Irssi::print( "load    load config stored in file.  loses changes made since last save." );

        Irssi::print( "\nValid config params:" );
        Irssi::print( "    private.enable = enable alerts for private messages" );
        Irssi::print( "    private.sticky = sticky alerts for private messages" );
        Irssi::print( "    hilight.enable = enable alerts for hilighted text" );
        Irssi::print( "    hilight.sticky = sticky alerts for hilighted text" );
        Irssi::print( "    public.channels #channelname sticky|nosticky" );

        Irssi::print( "\nExamples:" );
        Irssi::print( "/growl dump" );
        Irssi::print( "/growl save" );
        Irssi::print( "/growl load" );
        Irssi::print( "/growl config private.enable 0" );
        Irssi::print( "/growl config hilight.enable 1" );
        Irssi::print( "/growl config hilight.sticky 1" );
        Irssi::print( "/growl config public.channels #mychan sticky" );
        Irssi::print( "              messages to #mychan are enabled sticky" );
        Irssi::print( "/growl config public.channels #mychan delete" );
    
    }
}

Irssi::command_bind("growl", "growl");

#############################################################################
#_* Private Subroutines

#__* supress duplicates
# Store the last several messages.  If we get a duplicate message,
# then avoid sending multiple pages.  Duplicates occur when a message
# matches more than one message, e.g. a public message in a monitored
# channel that contains a hilighted word.
sub _supress_duplicates
{
    my ( $nick, $text, $sticky ) = @_;

    #Irssi::print( "Last seen: .$lastnick.:.$lasttext." );

    # already alerted for this one
    if (
        $lastnick &&
        $nick eq $lastnick &&
        $lasttext &&
        $text eq $lasttext
       ) {
        # Message was already alerted.  If this one is sticky and the
        # last one wasn't, then allow the alert to run again in sticky
        # mode.
        unless ( $sticky && ! $laststicky )
        {
            return 1;
        }
    
    }

    $lastnick = $nick;
    $lasttext = $text;
    $laststicky = $sticky;

    # no match
    return undef;
}

