Last active
November 19, 2015 20:25
-
-
Save reyjrar/8962790adcf40c6aced6 to your computer and use it in GitHub Desktop.
Email notifications for privmsgs, mentions, and hilights.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
use strict; | |
use warnings; | |
use Irssi; | |
use Irssi::Irc; | |
use MIME::Lite; | |
use Sys::Hostname; | |
use POSIX qw(strftime); | |
# Package Globals | |
our $VERSION = "201511a"; | |
our %IRSSI = ( | |
authors => "Brad Lhotsky", | |
contact => '[email protected]', | |
name => "emailnotifications.pl", | |
description => "Email notifications", | |
license => "BSD", | |
changed => "$VERSION" | |
); | |
our $DEBUG = exists $ENV{DEBUG} ? $ENV{DEBUG} : 0; | |
# Lexical Globals | |
my $email_to; | |
my $email_from; | |
my $email_tag; | |
my $interval; | |
my $max_msgs; | |
my $max_length; | |
my $template; | |
my $timeout_tag; | |
my %forward; | |
my %Templates = ( | |
summary => \&tmpl_summary, | |
full => \&tmpl_full, | |
sms => \&tmpl_sms, | |
); | |
my %TYPES = ( | |
privmsg => { | |
description => "Private Messages", | |
default_notify => 1, | |
priority => 1, | |
}, | |
mention => { | |
description => "Mentions", | |
default_notify => 1, | |
priority => 10, | |
}, | |
hilight => { | |
description => "Hilights", | |
default_notify => 0, | |
priority => 100, | |
}, | |
); | |
my @ORDERED_TYPES = sort { $TYPES{$a}->{priority} <=> $TYPES{$b}->{priority} } keys %TYPES; | |
# Email Headers | |
my %HEADERS = ( | |
'X-Automation-Function' => 'irssi-emailnotifications', | |
'X-Automation-Program' => 'Irssi::Script::emailnotifications', | |
'X-Automation-Server' => hostname(), | |
'Precedence' => 'bulk', | |
); | |
# Array for collecting data | |
my $is_over_max = 0; | |
my @data = (); | |
sub _debug { | |
return unless $DEBUG; | |
my @msgs = @_; | |
my @stack = caller 1; | |
my @time = gmtime; | |
foreach my $m (@msgs) { | |
printf "%s %s - %s", | |
strftime('%Y-%m-%dT%H:%M:%S',@time), | |
join('::',(split /::/, $stack[3])[-2,-1]), | |
$m; | |
} | |
} | |
sub initialize { | |
# Add our settings | |
Irssi::settings_add_str("emailnotifications", "email_to", ""); | |
Irssi::settings_add_str("emailnotifications", "email_from", sprintf "%s@%s", exists $ENV{USER} ? $ENV{USER} : 'irssi', hostname()); | |
Irssi::settings_add_int("emailnotifications", "email_interval", 60); | |
Irssi::settings_add_int("emailnotifications", "email_max_msgs", 50); | |
Irssi::settings_add_int("emailnotifications", "email_max_length", 72); | |
Irssi::settings_add_str("emailnotifications", "email_tag", "irssi"); | |
Irssi::settings_add_str("emailnotifications", "email_template", "summary"); | |
Irssi::settings_add_bool("emailnotifications", "email_debug", 0); | |
foreach my $t (keys %TYPES) { | |
Irssi::settings_add_bool("emailnotifications", "email_forward_$t", $TYPES{$t}->{default_notify}); | |
} | |
# Get their current values | |
$email_to = Irssi::settings_get_str("email_to"); | |
$email_from = Irssi::settings_get_str("email_from"); | |
$email_tag = Irssi::settings_get_str("email_tag"); | |
$interval = Irssi::settings_get_int("email_interval"); | |
$max_msgs = Irssi::settings_get_int("email_max_msgs"); | |
$max_length = Irssi::settings_get_int("email_max_length"); | |
$DEBUG = Irssi::settings_get_bool("email_debug"); | |
$template = Irssi::settings_get_str("email_template"); | |
$template = 'summary' unless exists $Templates{$template}; | |
foreach my $t (keys %TYPES) { | |
$forward{$t} = Irssi::settings_get_bool("email_forward_$t"); | |
} | |
# Handle proper scheduling of timeouts | |
Irssi::timeout_remove($timeout_tag) if defined $timeout_tag; | |
$timeout_tag = Irssi::timeout_add($interval * 1_000, \&send_message, undef); | |
} | |
sub send_message { | |
my @copy = @data; | |
# Clear Data | |
@data = (); | |
_debug("tick"); | |
# Nothing to report | |
return unless @copy; | |
_debug("received data"); | |
# We need an email_to | |
return unless defined $email_to and length $email_to > 0; | |
_debug("has email to set"); | |
# We need at least one true in this list | |
my @msgs = grep { $forward{$_->{type}} } @copy; | |
return unless @msgs; | |
_debug("forwarding set to dispatch the queue"); | |
# Get custom headers and body | |
my $cbTemplate = exists $Templates{$template} ? $Templates{$template} : $Templates{summary}; | |
my %headers = %HEADERS; | |
my $body; | |
eval { | |
$body = $cbTemplate->(\%headers,\@msgs); | |
}; | |
if(!defined $body) { | |
print "[emailnotifications] Failed sending $template: $@"; | |
return; | |
} | |
# Construct our message | |
my $msg = MIME::Lite->new( | |
From => $email_from, | |
To => $email_to, | |
Subject => sprintf("[%s] Received %d message%s while away", | |
$email_tag, | |
scalar(@msgs), | |
scalar(@msgs) == 1 ? '' : 's', | |
), | |
Type => 'TEXT', | |
); | |
# Load our headers | |
foreach my $k (keys %headers) { | |
$msg->add($k => $headers{$k}); | |
} | |
# Send the email | |
print "Sending $template email with updates."; | |
$msg->data($body); | |
$msg->send; | |
} | |
sub add_msg { | |
my ($msg) = @_; | |
if( @data >= $max_msgs ) { | |
printf("Over email_max_msgs[%d], discarding additional messages.", $max_msgs) | |
unless $is_over_max; | |
$is_over_max=1; | |
} | |
else { | |
$msg->{body} = substr($msg->{body},0,$max_length); | |
push @data, $msg; | |
printf("[%s] Queued %s from %s.", $msg->{on}, $msg->{type}, $msg->{by}); | |
} | |
} | |
sub priv_msg { | |
my ($server,$msg,$nick,$address,$target) = @_; | |
# Check away status | |
my $is_away = (defined $server->{away_reason} && length $server->{away_reason}); | |
return unless $is_away; | |
# Get chatnet | |
my $chatnet = exists $server->{chatnet} ? $server->{chatnet} | |
: exists $server->{tag} ? $server->{tag} | |
: exists $server->{address} ? $server->{address} | |
: 'the_void'; | |
# Data To Extract | |
add_msg({ | |
type => "privmsg", | |
by => $nick, | |
on => $chatnet, | |
body => $msg, | |
ts => time, | |
}); | |
} | |
sub hilight { | |
my ($dest, $text, $stripped) = @_; | |
if ($dest->{level} & MSGLEVEL_HILIGHT) { | |
my $nick = ""; | |
my $type = 'hilight'; | |
if($stripped =~ /(?:^<)(.+)(?:>)\s/) { | |
$nick = $1; | |
} | |
# For Private Messages | |
elsif( exists $dest->{target} && defined $dest->{target} ) { | |
$nick = $dest->{target}; | |
} | |
# Chatnet | |
my $chatnet = 'unknown'; | |
if( exists $dest->{server} && defined $dest->{server} ) { | |
my $server = $dest->{server}; | |
# Use eval to avoid dying if dereference fails here. | |
eval { | |
$chatnet = exists $server->{chatnet} ? $server->{chatnet} | |
: exists $server->{tag} ? $server->{tag} | |
: exists $server->{address} ? $server->{address} | |
: 'the_void'; | |
}; | |
eval { | |
if( exists $server->{nick} | |
&& defined $server->{nick} | |
&& index($stripped,$server->{nick}) >= 0 | |
) { | |
$type = 'mention'; | |
} | |
}; | |
# Skip unless away. | |
return unless defined $server->{away_reason} && length $server->{away_reason}; | |
} | |
# Data To Extract | |
add_msg({ | |
type => $type, | |
by => $nick, | |
on => $chatnet, | |
body => $stripped, | |
ts => time, | |
}); | |
} | |
} | |
sub tmpl_sms { | |
my ($headers,$msgs) = @_; | |
_debug("generating content"); | |
my %chatnet = (); | |
foreach my $m ( @{$msgs} ) { | |
# Instantiate the counters. | |
$chatnet{$m->{on}} ||= { | |
privmsg => {}, | |
mention => {}, | |
hilight => {}, | |
total => 0, | |
}; | |
my $ref = $chatnet{$m->{on}}; | |
# Count things | |
$ref->{$m->{type}}{$m->{by}} ||= 0; | |
$ref->{$m->{type}}{$m->{by}}++; | |
$ref->{total}++; | |
} | |
# Generate the output | |
my $output = ''; | |
foreach my $c (sort { $chatnet{$b}->{total} <=> $chatnet{$a}->{total} } keys %chatnet) { | |
$output .="N=$c"; | |
foreach my $type (@ORDERED_TYPES) { | |
# Skip if there's nothing here | |
next unless scalar(keys %{ $chatnet{$c}->{$type} }); | |
my $msgs = $chatnet{$c}->{$type}; | |
$output .="($type:"; | |
$output .= join(',', sort keys %{$msgs}); | |
$output .=")"; | |
} | |
$output .= "| "; | |
} | |
return $output; | |
} | |
sub tmpl_summary { | |
my ($headers,$msgs) = @_; | |
_debug("generating content"); | |
my %chatnet = (); | |
foreach my $m ( @{$msgs} ) { | |
# Instantiate the counters. | |
$chatnet{$m->{on}} ||= { | |
privmsg => {}, | |
mention => {}, | |
hilight => {}, | |
total => 0, | |
}; | |
my $ref = $chatnet{$m->{on}}; | |
# Count things | |
$ref->{$m->{type}}{$m->{by}} ||= 0; | |
$ref->{$m->{type}}{$m->{by}}++; | |
$ref->{total}++; | |
} | |
# Generate the output | |
my @output = (); | |
foreach my $c (sort { $chatnet{$b}->{total} <=> $chatnet{$a}->{total} } keys %chatnet) { | |
push @output, sprintf "Network:%s - %d message%s", $c, $chatnet{$c}->{total}, $chatnet{$c}->{total} == 1 ? '' : 's'; | |
foreach my $type (@ORDERED_TYPES) { | |
# Skip if there's nothing here | |
next unless scalar(keys %{ $chatnet{$c}->{$type} }); | |
my $msgs = $chatnet{$c}->{$type}; | |
push @output, | |
" $TYPES{$type}->{description}"; | |
foreach my $k (sort { $msgs->{$b} <=> $msgs->{$b} } keys %{$msgs}) { | |
push @output, sprintf(" %s - %d time%s", $k, $msgs->{$k}, $msgs->{$k} == 1 ? '' : 's'); | |
} | |
push @output, ''; | |
} | |
} | |
return join('', map { chomp; "$_\n" } @output); | |
} | |
sub tmpl_full { | |
my ($headers,$msgs) = @_; | |
_debug("generating content"); | |
# Set confidenitality header: | |
$headers->{Sensitivity} = 'company confidential'; | |
my %chatnet = (); | |
foreach my $m ( @{$msgs} ) { | |
# Instantiate the counters. | |
$chatnet{$m->{on}} ||= { | |
privmsg => {}, | |
mention => {}, | |
hilight => {}, | |
total => 0, | |
}; | |
my $ref = $chatnet{$m->{on}}; | |
# Count things | |
$ref->{$m->{type}}{$m->{by}} ||= []; | |
push @{ $ref->{$m->{type}}{$m->{by}} }, | |
sprintf "%s %s: %s", | |
strftime('%H:%M:%S',localtime($m->{ts})), | |
$m->{by}, | |
$m->{body}; | |
$ref->{total}++; | |
} | |
# Generate the output | |
my @output = (); | |
foreach my $c (sort { $chatnet{$b}->{total} <=> $chatnet{$a}->{total} } keys %chatnet) { | |
push @output, sprintf "Network:%s - %d message%s", $c, $chatnet{$c}->{total}, $chatnet{$c}->{total} == 1 ? '' : 's'; | |
foreach my $type (@ORDERED_TYPES) { | |
# Skip if there's nothing here | |
next unless scalar(keys %{ $chatnet{$c}->{$type} }); | |
my $msgs = $chatnet{$c}->{$type}; | |
push @output, | |
" $TYPES{$type}->{description}"; | |
foreach my $k (sort { @{$msgs->{$b}} <=> @{$msgs->{$b}} } keys %{$msgs}) { | |
map { push @output, " $_"; } @{ $msgs->{$k} }; | |
} | |
push @output, ''; | |
} | |
push @output, '-----------------', ''; | |
} | |
return join('', map { chomp; "$_\n" } @output); | |
} | |
initialize(); | |
Irssi::signal_add("setup changed", "initialize"); | |
Irssi::signal_add_last("message private", "priv_msg"); | |
Irssi::signal_add_last("print text", "hilight"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment