use strict;
use warnings;
# {{{ DOCUMENTATION
#####
# A lot of code borrowed from the prowlnotify.pl script at
# http://www.denis.lemire.name/2009/07/07/prowl-irssi-hack/
# and pushovernotify.pl (Daniel Johansson <donnex@donnex.net>)
# and http://www.geekfarm.org/wu/muse/scripts/growl-notify.txt
#
# https://gist.github.com/3714183
#
# }}}
use Irssi;
use Irssi::Irc;
use vars qw($VERSION %IRSSI %config);
use HTTP::Request;
use LWP::UserAgent;
use JSON;
$VERSION = '0.3.1';
%IRSSI = (
authors => 'Justin J. Novack',
contact => 'jnovack@gmail.com',
name => 'pushover',
description => 'Send a Pushover notification',
license => 'GPLv2',
url => 'http://www.ozmonet.com'
);
# Awful updating hack - Remember to increase this number on each revision!
# This counts the number of gist revisions and compares to current version.
Irssi::settings_add_str($IRSSI{'name'}, 'pushover_gist', '6');
# Generics
$config{away_level} = 0;
$config{awayreason} = 'Auto-away';
$config{clientcount} = 0;
# Keep last message to not repeat it.
my $lastnick = "";
my $lasttext = "";
# {{{ PUBLIC FUNCTIONS
# {{{ message(text)
sub message
{
my $text = shift;
Irssi::print('%b>> %B'.$IRSSI{'name'}."%n :: $text");
}
# }}}
# {{{ error(text)
sub error
{
my $text = shift;
error("%y[%YWARNING%y]%n :: $text");
}
# }}}
# {{{ warning(text)
sub warning
{
my $text = shift;
message("%y[%YWARNING%y]%n :: $text");
}
# }}}
# {{{ debug(text)
sub debug
{
my $text = shift;
return unless Irssi::settings_get_bool('pushover_debug');
message("%w[%WDEBUG%w]%n :: $text");
}
# }}}
# {{{ send_pushover(title, message)
sub send_pushover
{
my ($title, $message) = @_;
my $api_usr = Irssi::settings_get_str('pushover_api_usr');
my $api_app = Irssi::settings_get_str('pushover_api_app');
if (!$api_usr || !$api_app) {
error('Notification not sent. » Missing pushover user or api key(s).');
return;
}
my $response = LWP::UserAgent->new()->post(
'https://api.pushover.net/1/messages.json',
[
token => $api_app,
user => $api_usr,
title => $title,
message => $message,
]
);
#my $response = $ua->request($req);
if ($response->is_success) {
debug("Notification successfully posted. » $message");
} elsif ($response->code == 429) {
warning('Notification not permitted. » Application is over API limit.');
} elsif ($response->code == 400) {
warning('Notification not posted. » Incorrect user or application key.');
debug($response->content);
} else {
warning('Notification not posted. » ('.$response->code.') '.$response->decoded_content);
}
}
# }}}
# {{{ client_connect()
sub client_connect
{
my (@servers) = Irssi::servers;
$config{clientcount}++;
debug('Client connected.');
# setback
foreach my $server (@servers) {
# if you're away on that server send yourself back
if ($server->{usermode_away} == 1) {
$server->send_raw('AWAY :');
}
}
}
# }}}
# {{{ client_disconnect()
sub client_disconnect
{
my (@servers) = Irssi::servers;
debug('Client disconnected.');
$config{clientcount}-- unless $config{clientcount} == 0;
# setaway
if ($config{clientcount} <= $config{away_level}) {
# we have the away_level of clients connected or less.
foreach my $server (@servers) {
if ($server->{usermode_away} == '0') {
# we are not away on this server. set the autoaway reason
$server->send_raw(
'AWAY :' . $config{awayreason}
);
}
}
}
}
# }}}
# {{{ msg_pub(server, data, nick, mask, target)
sub msg_pub
{
my ($server, $data, $nick, $mask, $target) = @_;
my $safeNick = quotemeta($server->{nick});
$data = _strip_formatting($data);
if ($server->{usermode_away} == '1' && $data =~ /$safeNick/i) {
debug('User is away?' . $server->{usermode_away});
unless ( _supress_duplicates( $nick, $data) ) {
debug("Public Message » (".$server->{tag}.") $target : <$nick> $data");
send_pushover($target,"<$nick> $data");
}
}
}
# }}}
# {{{ msg_pri(server, data, nick, address)
sub msg_pri
{
my ($server, $data, $nick, $address) = @_;
$data = _strip_formatting($data);
if ($server->{usermode_away} == '1') {
debug('User is away?' . $server->{usermode_away});
unless ( _supress_duplicates( $nick, $data) ) {
debug("Private Message » (".$server->{tag}.") <$nick> $data");
send_pushover($nick, $data);
}
}
}
# }}}
# {{{ msg_hi(dest, text, stripped)
sub msg_hi
{
my ($dest, $text, $stripped) = @_;
my $target = "";
my $hilights = Irssi::settings_get_bool('pushover_hilight');
if (($dest->{level} & (MSGLEVEL_HILIGHT|MSGLEVEL_MSGS))
&& ($dest->{level} & MSGLEVEL_NOHILIGHT) == 0
&& $hilights)
{
# Pull nick and text from $stripped
$stripped =~ m|^\<(.*?)\>\s+(.*)$|;
my ( $nick, $text ) = ( $1, $2 );
if ($dest->{level} & MSGLEVEL_PUBLIC) {
$target = $dest->{target};
}
return undef unless ( $nick && $text );
unless ( _supress_duplicates( _strip_nick($nick), $text) ) {
debug("Highlight » $target <$nick> $text");
send_pushover($target, "<$nick> $text");
}
}
}
# }}}
# {{{ cmd_pushover(data, server, witem)
sub cmd_pushover
{
my ($data, $server, $witem) = @_;
my ($op, $var1) = split " ", $data;
$op = lc $op;
if (!$op) {
message(" %9/pushover test%n - Sends a test message to pushover.");
message(" %9/pushover debug 0%n - Turns debugging off.");
message(" %9/pushover debug 1%n - Turns debugging on. (default)");
message(" %9/pushover exact 0%n - Suppress repeat notifications from the same user.");
message(" %9/pushover exact 1%n - Only suppress exact duplicate messages. (default)");
message(" %9/pushover hilight 0%n - Turns off hilight notifications. (default)");
message(" %9/pushover hilight 1%n - Turns on hilight notifications.");
} elsif ($op eq "test") {
debug("A message is being sent to pushover, please confirm receipt.");
send_pushover("Testing", "It works! Yay!");
} elsif ($op eq "debug" && $var1 == "0") {
Irssi::settings_set_bool('pushover_debug', 0);
message('Debugging has been turned %9OFF%n');
} elsif ($op eq "debug" && $var1 == "1") {
Irssi::settings_set_bool('pushover_debug', 1);
message('Debugging has been turned %9ON%n');
} elsif ($op eq "exact" && $var1 == "0") {
Irssi::settings_set_bool('pushover_exact', 0);
message('Duplicate checking is %9user%n based. If the last message sent was from the same user, it will be suppressed.');
} elsif ($op eq "exact" && $var1 == "1") {
Irssi::settings_set_bool('pushover_exact', 1);
message('Duplicate checking is %9user+text%n based. Messages are only suppressed if they are exactly the same.');
} elsif ($op eq "hilight" && $var1 == "0") {
Irssi::settings_set_bool('pushover_hilight', 0);
message('Sending Pushover notifications for hilights is %9OFF%n.');
} elsif ($op eq "hilight" && $var1 == "1") {
Irssi::settings_set_bool('pushover_hilight', 1);
message('Sending Pushover notifications for hilights is %9ON%n.');
} elsif ($op eq "last") {
message("\$lastnick was: $lastnick");
message("\$lasttext was: $lasttext");
}
}
# }}}
# }}}
# {{{ main()
Irssi::settings_add_str($IRSSI{'name'}, 'pushover_api_app', '');
Irssi::settings_add_str($IRSSI{'name'}, 'pushover_api_usr', '');
Irssi::settings_add_bool($IRSSI{'name'}, 'pushover_debug', 1);
Irssi::settings_add_bool($IRSSI{'name'}, 'pushover_exact', 1);
Irssi::settings_add_bool($IRSSI{'name'}, 'pushover_hilight', 0);
Irssi::signal_add_last('proxy client connected', 'client_connect');
Irssi::signal_add_last('proxy client disconnected', 'client_disconnect');
Irssi::signal_add_last('message public', 'msg_pub');
Irssi::signal_add_last('message private', 'msg_pri');
Irssi::signal_add_last("print text", "msg_hi");
Irssi::command_bind('pushover',\&cmd_pushover);
Irssi::print('%G>>%n '.$IRSSI{name}.'%n v'.$VERSION.' loaded.');
if (!Irssi::settings_get_str('pushover_api_usr')) {
warning('user api key is not set, set it with %9/set pushover_api_usr apikey');
}
if (!Irssi::settings_get_str('pushover_api_app')) {
warning('app api key is not set, set it with %9/set pushover_api_app apikey');
}
message('Type %9/pushover%n for help.');
if (Irssi::settings_get_bool('pushover_debug') == "1") {
debug(' Debugging is on, type %9/pushover debug 0%n to turn off');
}
if (Irssi::settings_get_bool('pushover_exact') == "1") {
message('Duplicate checking is %9user+text%n based. Messages are only suppressed if they are exactly the same.');
} else {
message('Duplicate checking is %9user%n based. If the last message sent was from the same user, it will be suppressed.');
}
if (Irssi::settings_get_bool('pushover_hilight') == "1") {
message('Sending Pushover notifications for hilights is %9ON%n.');
} else {
message('Sending Pushover notifications for hilights is %9OFF%n.');
}
### Check for Update
# This is an extremely AWFUL way to check for updates.
# Counts the revisions at github and compares to hardcoded value.
#
my $response = LWP::UserAgent->new()->get('https://api.github.com/gists/3714183');
my $decoded_json = decode_json( $response->decoded_content );
if (Irssi::settings_get_str('pushover_gist') != $#{$decoded_json->{history}}+1) {
warning('You are not up to date! Please download the newest version from %9https://gist.github.com/3714183%n');
}
# }}}
# {{{ PRIVATE
# {{{ _supress_duplicates(nick, text)
#
# 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 ) = @_;
# Check both nick and text for matches only if pushover_exact is set
if ( Irssi::settings_get_bool('pushover_exact') eq "1" &&
$lastnick && $nick eq $lastnick &&
$lasttext && $text eq $lasttext
)
{
# debug("supressed_duplicates(exact)");
return 1;
}
# Otherwise, check just nick is set
elsif ( $lastnick && $nick eq $lastnick )
{
# debug("supressed_duplicates(loose)");
return 1;
}
# debug("\$lastnick : $lastnick || \$nick : $nick");
# debug("\$lasttext : $lasttext || \$text : $text");
$lastnick = $nick;
$lasttext = $text;
# no match
return undef;
}
# }}}
# {{{ _strip_nick(nick)
#
# Strips the prepended spaces and modes from the nickname. The
# nick can contain modes, spaces or other characters if the
# hilight function adds formatting. This removes it so a dup
# message can be properly filtered out.
#
sub _strip_nick
{
my ($nick) = shift;
$nick =~ tr/[^a-zA-Z0-9\-\_\[\]\{\}\|\`\^]+//cd;
return $nick;
}
# }}}
# {{{ _strip_formatting(msg)
#
# Strips formatting from the message so it can be cleanly
# debugged or sent to the client.
#
sub _strip_formatting
{
my ($msg) = shift;
$msg =~ s/\x03[0-9]{0,2}(,[0-9]{1,2})?//g;
$msg =~ s/[^\x20-\xFF]//g;
$msg =~ s/\xa0/ /g;
return $msg;
}
# }}}}}}