aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xculosillad.pl152
1 files changed, 152 insertions, 0 deletions
diff --git a/culosillad.pl b/culosillad.pl
new file mode 100755
index 0000000..74d2297
--- /dev/null
+++ b/culosillad.pl
@@ -0,0 +1,152 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use File::Basename;
+use HTTP::Tiny;
+use JSON::PP qw<decode_json>;
+
+my @REQUIRED_CONFIG_KEYS = qw(url user token);
+
+sub usage {
+ die <<EOF
+Usage: $0 [OPTIONS] {start|stop}
+Options:
+ --cron <minutes> wait randomly from 0 to <minutes> before doing anything
+ -n | --dry-run fetch data but don't change anything
+ -v | --verbose increase log level
+
+Sample config file (~/.config/culosillad/fulanito.conf):
+
+ url = https://time.example.com
+ user = fulanito
+ token = 123456789
+EOF
+ ;
+}
+
+# Parse CLI arguments
+my $DRYRUN = my $CRON = my $VERBOSE = 0;
+my $MODE;
+while (my $opt = shift) {
+ if ($opt eq '--cron') {
+ $CRON = int(shift) || die "Invalid argument for '--cron'\n";
+ } elsif ($opt eq '-v' || $opt eq '--verbose') {
+ $VERBOSE = 1;
+ } elsif ($opt eq '-n' || $opt eq '--dryrun') {
+ $DRYRUN = 1;
+ } elsif ($opt eq 'start' || $opt eq 'stop') {
+ $MODE = $opt;
+ }
+}
+usage unless $MODE;
+
+my $CONFDIR = $ENV{'CULOSILLAD_CONFDIR'} ||
+ ($ENV{'XDG_CONFIG_HOME'} || glob '~/.config') . '/culosillad';
+mkdir $CONFDIR;
+print "[+] Configs dir: $CONFDIR\n" if $VERBOSE == 1;
+
+my @CONFIGS = glob("$CONFDIR/*");
+
+sub read_config {
+ my $cfgfile = shift;
+ my %config;
+
+ print "[+] Reading config $cfgfile\n" if $VERBOSE == 1;
+
+ open(my $fh, '<', $cfgfile) or die "Could not open file: $!";
+ my $line = 1;
+ while (<$fh>) {
+ if (/^([a-z]+)\s*=\s*(\S+)$/) {
+ $config{$1} = $2;
+ } else {
+ die "Error reading config $cfgfile (line=$line)\n"
+ }
+ $line += 1;
+ }
+ foreach my $key (@REQUIRED_CONFIG_KEYS) {
+ die "Missing key '$key' in config file\n" unless exists $config{$key};
+ }
+ return \%config;
+}
+
+my %CONFIG;
+
+sub request {
+ my ($method, $endpoint) = @_;
+
+ my $response = HTTP::Tiny->new(
+ agent => 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0'
+ )->request(
+ $method,
+ "$CONFIG{url}/api/$endpoint",
+ {
+ headers => {
+ 'X-AUTH-USER' => $CONFIG{user},
+ 'X-AUTH-TOKEN' => $CONFIG{token},
+ }
+ }
+ );
+ my $content = decode_json($response->{content});
+ $response->{status} eq '200' or die "Error calling '$endpoint': '$content->{message}'\n";
+ return $content;
+}
+
+sub get_recent_timesheets {
+ return request('GET', 'timesheets/recent');
+}
+
+sub get_active_timesheets {
+ return request('GET', 'timesheets/active');
+}
+
+sub get_active_ets {
+ my $timesheets = get_active_timesheets();
+ return grep { $_->{project}{name} eq "Fichaje_Empleados" } @$timesheets;
+}
+
+sub get_ets {
+ my $timesheets = get_recent_timesheets();
+ return grep { $_->{project}{name} eq "Fichaje_Empleados" } @$timesheets;
+}
+
+sub start_ets {
+ my $tsid = shift;
+ return request('PATCH', "timesheets/$tsid/restart");
+}
+
+sub stop_ets {
+ my $tsid = shift;
+ return request('PATCH', "timesheets/$tsid/stop");
+}
+
+print "[+] DRY RUN: not doing anything\n" if $DRYRUN == 1;
+
+if ($CRON > 0) {
+ my $minutes = int(rand($CRON));
+ printf "[+] Cron mode enabled (max: %s minutes); sleeping %s minutes...\n", $CRON, $minutes;
+ sleep $minutes * 60;
+}
+
+foreach my $cfgfile (@CONFIGS) {
+ next unless -f $cfgfile; # skip directories
+
+ %CONFIG = %{ read_config($cfgfile) };
+
+ my ($ets) = get_active_ets();
+
+ if ($MODE eq 'start') {
+ die "Refusing to start; found active ETS ($ets->{id})\n" if defined $ets;
+
+ my ($ets) = get_ets();
+ die "Unable to find ETS\n" unless defined $ets;
+
+ start_ets($ets->{id}) unless $DRYRUN == 1;
+ print "[+] OK! Started successfully\n";
+ } elsif ($MODE eq 'stop') {
+ die "Unable to find active timesheet\n" unless defined $ets;
+ stop_ets($ets->{id}) unless $DRYRUN == 1;
+ print "[+] OK! Stopped successfully\n";
+ }
+}