Skip to content

Instantly share code, notes, and snippets.

@reyjrar
Last active November 19, 2015 20:25
Show Gist options
  • Save reyjrar/8962790adcf40c6aced6 to your computer and use it in GitHub Desktop.
Save reyjrar/8962790adcf40c6aced6 to your computer and use it in GitHub Desktop.
Email notifications for privmsgs, mentions, and hilights.
#!/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