#!/usr/bin/env perl
# -*- perl -*-
=head1 NAME

freemobile - A plugin to monitor a freemobile subscription

=head1 INSTALLATION

Create a link to this script in /etc/munin/plugins/ :
$ ln -s /path/to/freemobile-munin /etc/munin/plugins/freemobile


=head1 CONFIGURATION

You need to configure the plugin like that:

    [freemobile]
    user florent
    env.HOME /home/florent
    env.freemonitored voice sms mms data specialvoice
    env.cache_expire 3600
    timeout 30

C<user> I<required>: user with freemobile backend configured

C<env.HOME> I<required>: path to user home

C<env.freemonitored> (optional): default only 'voice sms'
The full list of monitored options is :
 * voice sms mms data specialvoice voicetoint voiceint smsint mmsint dataint

C<env.cache_expire 3600> (optional): cache interval in second, or time
between two connection to the website. The cache interval is 3 hours by default.

C<timeout 30> (optional): Munin internal option. The plugin can be slow,
30s is recommended.

=head1 LICENSE

AGPLv3

=cut
use strict;
use warnings;
use Carp;
use English qw(-no_match_vars);
use encoding 'iso-8859-1';    # Munin doesn't like utf-8 :-(
use Encode;

my @monitored = split / /, $ENV{'freemonitored'} || 'voice sms';
my $cachedir  = $ENV{'HOME'} . '/.config/weboob/munin/';
my $cachefile = "$cachedir/freemobile-munin";

my $refreshtime = $ENV{'cache_expire'} || 10_800;
my $weboob = '/usr/bin/env weboob-cli ICapBill get_details coin -f table';
my $cache_fh;

my %label = (
    'voice'        => 'Voix en France (min)',
    'voicetoint'   => 'Voix vers l\'international (min)',
    'specialvoice' => 'Numéros spéciaux (min)',
    'sms'          => 'SMS en France',
    'mms'          => 'MMS en France',
    'data'         => 'Data en France',
    'voiceint'     => 'Voix à l\'international (min)',
    'smsint'       => 'SMS à l\'international',
    'mmsint'       => 'MMS à l\'international',
    'dataint'      => 'Data à l\'international',
);

my %linenum = (
    'voice'        => 3,
    'voicetoint'   => 3,
    'specialvoice' => 4,
    'sms'          => 5,
    'mms'          => 6,
    'data'         => 7,
    'voiceint'     => 8,
    'smsint'       => 9,
    'mmsint'       => 10,
    'dataint'      => 11,
);

my %regexp = (
    'voice'        => 'National : (\d+)h(\d+)min(\d+)s',
    'voicetoint'   => 'International : (\d+)h(\d+)min(\d+)s',
    'specialvoice' => '\| (\d+)h(\d+) min (\d+)s',
    'sms'          => 'Conso SMS                  \| (\d+) \/ (.*)',
    'mms'          => 'Vous avez consommé (\d+) MMS',
    'data'         => 'Vous avez consommé ([\d\-\.]+) Mo',
    'voiceint'     => 'Appels émis (\d+)h(\d+)min(\d+)s',
    'smsint'       => 'Conso SMS (international)  \| (\d+)',
    'mmsint'       => 'Vous avez consommé (\d+) MMS',
    'dataint'      => 'Vous avez consommé ([\d\-]+) Mo',
);

my %post = (
    'voice'        => 'postvoice',
    'voicetoint'   => 'postvoice',
    'specialvoice' => 'postvoice',
    'sms'          => 'simplepost',
    'mms'          => 'simplepost',
    'data'         => 'simplepost',
    'voiceint'     => 'postvoice',
    'smsint'       => 'simplepost',
    'mmsint'       => 'simplepost',
    'dataint'      => 'simplepost',
);

sub doubleprint {
    my $var = shift;
    print {$cache_fh} $var;
    print $var;
    return 0;
}

sub postvoice {
    my @args    = @_;
    my $minutes = $args[0] * 60 + $args[1] + $args[2] / 60;
    doubleprint "$minutes \n";
    return 0;
}

sub simplepost {
    my @args = @_;
    doubleprint "$args[0] \n";
    return 0;
}

sub config {
    binmode STDOUT, ':encoding(iso-8859-1)';
    print <<'EOF';
graph_title Conso Free
graph_vlabel Suivi conso du forfait Free Mobile
graph_category weboob
graph_args -l 0
EOF
    foreach (@monitored) {
        print "$_.label $label{$_}\n";
    }
    return 0;
}

sub fetch {
    my @cache_data;
    # Check if cache exist and not older than the refresh threshold.
    if ( open $cache_fh, '<', $cachefile ) {
        @cache_data = <$cache_fh>;
        close $cache_fh or croak "unable to close: $ERRNO";

        # File not empty?
        if ( @cache_data > 0 ) {

            # Data is still fresh. Display cached values and exit.
            if ( time - $cache_data[0] < $refreshtime ) {
                print join q{}, @cache_data[ 1 .. $#cache_data ];
                exit 0;
            }
        }
    }
    # execute weboob
    open my $data, q(-|), $weboob or croak "Couldn't execute program: $ERRNO";
    my @lines = <$data>;
    close $data or carp "unable to close: $ERRNO";
    # If error output, print the cache (if exist) and exit
    if ( @lines == 0 ) {
        if ( @cache_data > 0 ) {
            print join q{}, @cache_data[ 1 .. $#cache_data ];
            exit 0;
        }
        exit 1;
    }

    # Open cache for writing
    open $cache_fh, '>', $cachefile
      or croak "Failed to open file $cachefile";
    print {$cache_fh} time . "\n";

    foreach my $monit (@monitored) {
        doubleprint "$monit.value ";
        if ( my @results = $lines[ $linenum{$monit} ] =~ /$regexp{$monit}/ ) {
            my $postfunction = $post{$monit};
            &{ \&$postfunction }(@results);
        }
        else {
            doubleprint "0 \n";
        }
    }
    close $cache_fh or croak "unable to close: $ERRNO";
    return 0;
}

# Create the munin cache dir if missing
if ( !-d $cachedir ) {
    mkdir $cachedir;
}

if ( $ARGV[0] and $ARGV[0] eq 'config' ) {
    config;
}
else {
    fetch;
}

__END__
