--- ChivanetModcon/modcon 2025/01/31 13:38:51 1.2 +++ ChivanetModcon/modcon 2025/02/01 03:17:46 1.5 @@ -4,13 +4,22 @@ # ChivaNet Moderator Console # Copyright (C) 2025 Coherent Logic Development LLC # -# $Id: modcon,v 1.2 2025/01/31 13:38:51 snw Exp $ +# $Id: modcon,v 1.5 2025/02/01 03:17:46 snw Exp $ # # Author: Serena Willis # # Licensed AGPL-3.0 # # $Log: modcon,v $ +# Revision 1.5 2025/02/01 03:17:46 snw +# Fix session list +# +# Revision 1.4 2025/01/31 19:41:00 snw +# Move to a UNIX UI paradigm +# +# Revision 1.3 2025/01/31 15:39:06 snw +# Minor fixes +# # Revision 1.2 2025/01/31 13:38:51 snw # Initial basic functions working # @@ -21,61 +30,45 @@ use REST::Client; use JSON; -use Getopt::Long; use Data::Dumper; use Term::ReadKey; +use Getopt::Long; -my $rasurl = ''; +my $sso_account = ''; my $apikey = ''; my $online = 0; my $modcon_version = '0.0.1'; my $cnclient = ''; -my $rasclient = ''; - my $wchar = ''; my $hchar = ''; my $wpixels = ''; my $hpixels = ''; - my $account = ''; +my $mode = '---'; +my $user = '---'; +$site = ''; + +sub list_directories { + return ('sso', 'ras'); +} sub list_sso_users { - $cnclient->GET("/chivanet/users"); - my $ct = 0; - + $cnclient->GET("/chivanet/users"); my $json = $cnclient->responseContent(); my $hashref = decode_json($json); if($hashref->{ok} == 1) { my $arrayref = $hashref->{users}; - my @users = sort(@{$arrayref}); - - foreach my $user (@users) { - print "$user\n"; - $ct = $ct + 1; - - if($ct > $hchar - 2) { - print "ENTER to continue, Q to quit..."; - my $resp = ; - chomp($resp); - if($resp eq "Q") { - return; - } - elsif($resp eq "q") { - return; - } - $ct = 0; - } - } + my @users = @{$arrayref}; + + return @users; } else { print "RPC error\n"; + return (); } } -sub list_ras_users { - -} sub select_sso_user { my($id) = @_; @@ -83,22 +76,18 @@ sub select_sso_user { $cnclient->GET("/chivanet/validate_user?id=$id"); my $json = $cnclient->responseContent(); my $hashref = decode_json($json); - - #print Dumper($hashref); if($hashref->{exists} == 1) { $cnclient->GET("/chivanet/user?id=$id"); my $json = $cnclient->responseContent(); $account = decode_json($json); - + return $id; } else { - print ">>> invalid SSO user $id [$hashref->{error}]\n"; return "---"; - } - + } } sub select_ras_user { @@ -112,7 +101,6 @@ sub select_ras_user { return $id; } else { - print ">>> invalid RAS screen name $id\n"; return "---"; } } @@ -127,8 +115,7 @@ sub trace_ras_sn { my $result = $record->{id}; print "RAS screen name $user belongs to SSO account $result; switching to SSO mode\n"; - return $result; - + return $result; } sub list_ras_sessions { @@ -137,80 +124,52 @@ sub list_ras_sessions { my $hashref = decode_json($json); my $sessions = $hashref->{sessions}; my $arrayref = $sessions->{sessions}; - my $ct = 0; + my @result = (); foreach my $session (@{$arrayref}) { - print "$session->{screen_name}\n"; - - $ct = $ct + 1; - - if($ct > $hchar - 2) { - print "ENTER to continue, Q to quit..."; - my $resp = ; - chomp($resp); - if($resp eq "Q") { - return; - } - elsif($resp eq "q") { - return; - } - $ct = 0; - } + push(@result, $session->{id}); } - print "$sessions->{count} current sessions\n"; - + return @result; } sub list_ras_screennames { my($id) = @_; - - $cnclient->GET("/chivanet/ras_screen_names?id=$id"); + my @result = (); + $cnclient->GET("/chivanet/user_ras_screen_names?id=$id"); my $json = $cnclient->responseContent(); my $hashref = decode_json($json); my $arrayref = $hashref->{screen_names}; - my $ct = 0; + + foreach my $item (@{$arrayref}) { + push(@result, $item->{screen_name}); + } + - foreach my $sn (@{$arrayref}) { - my $name = "$sn->{screen_name}\n"; - chomp($name); + return @result; +} - my $bot = ''; - my $public = ''; - - if($sn->{bot_sn} eq 1) { - $bot = "bot"; - } - else { - $bot = "human"; - } +sub list_ras_users { - if($sn->{public_sn} eq 1) { - $public = "public"; - } - else { - $public = "unlisted"; - } + my @result = (); + + $cnclient->GET("/chivanet/all_ras_screen_names"); + my $json = $cnclient->responseContent(); + my $hashref = decode_json($json); - print "$name: $public $bot; created $sn->{create_ts}\n"; - - $ct = $ct + 1; + if($hashref->{ok} == 0) { + print "RPC error\n"; + return @result; + } + + my $arrayref = $hashref->{screen_names}; - if($ct > $hchar - 2) { - print "ENTER to continue, Q to quit..."; - my $resp = ; - chomp($resp); - if($resp eq "Q") { - return; - } - elsif($resp eq "q") { - return; - } - $ct = 0; - } + foreach my $entryref (@{$arrayref}) { + push(@result, $entryref->{id}); } + return @result; } sub print_sso_user { @@ -268,9 +227,7 @@ sub print_ras_user { } else { print "Screen Name : $id [$online]\n\n"; - } - -# print Dumper($status); + } } @@ -293,57 +250,194 @@ sub send_im { } } +sub ls { + my ($description, @entries) = @_; + + my @sorted = sort(@entries); + my $maxlen = 0; + foreach my $entry (@sorted) { + my $len = length($entry); + if ($len > $maxlen) { + $maxlen = $len; + } + } + $maxlen = $maxlen + 2; + my $ct = $#sorted + 1; + foreach my $entry (@sorted) { + if($col + $maxlen >= $wchar) { + print "\n"; + $col = 0; + } + else { + printf("%-$maxlen\s", $entry); + $col = $col + $maxlen; + } + } + print "\n$ct items in $description\n"; + +} + sub prompt { my $rawcmd = ''; - my $mode = '---'; - my $user = '---'; - + my $pmode = $mode; + my @path = (); + while (1) { - print "MODCON [$mode/$user]> "; + $pmode = lc $mode; + my $prompt = ''; + if($pmode ne "---") { + if($user eq '---') { + $prompt = "$sso_account\@$site:[/$pmode]\$ "; + } + else { + $prompt = "$sso_account\@$site:[/$pmode/$user]\$ "; + } + } + else { + $prompt = "$sso_account\@$site:[/]\$ "; + } + print $prompt; my $line = ; chomp($line); $rawcmd = $line; my @cmd = split(' ', $rawcmd); - if ($cmd[0] eq "quit") { + if ($cmd[0] eq "exit" || $cmd[0] eq "logout" || $cmd[0] eq "quit" || $cmd[0] eq "bye" || $cmd[0] eq "lo") { return; } - elsif ($cmd[0] eq "mode") { - if($cmd[1] eq "sso") { - $mode = "SSO"; - $user = "---"; - $account = ''; - } - elsif ($cmd[1] eq "ras") { - $mode = "RAS"; - $user = "---"; - $account = ''; - } - else { - print "?\n"; - } - } - elsif ($cmd[0] eq "select") { - if($cmd[1] eq "user") { - if($mode eq "SSO") { - $user = select_sso_user($cmd[2]); - chomp($user); - print_sso_user; - } - elsif($mode eq "RAS") { - $user = select_ras_user($cmd[2]); - chomp($user); - print_ras_user $user; + elsif ($cmd[0] eq "pwd") { + my $pstr = join('/', @path); + print "/$pstr\n"; + } + elsif ($cmd[0] eq "cd") { + my $abspath = false; + + if($cmd[1] eq "..") { + if(@path) { + if($#path == 1) { + $user = "---"; + @path = ($pmode); + } + elsif($#path == 0) { + $mode = "---"; + @path = (); + } } else { - print "?\n"; + print "already in root directory\n"; } - } - else { - print "?\n"; } + else { + my @oldpath = @path; + @path = split('/', $cmd[1]); + + if(substr($cmd[1], 0, 1) eq '/') { + $abspath = true; + shift @path; + } + + if($abspath eq true || !@oldpath) { + if($#path == 0) { + # mode, no user + if($path[0] eq "sso") { + $mode = "SSO"; + $user = "---"; + @path = ('sso'); + } + elsif($path[0] eq "ras") { + $mode = "RAS"; + $user = "---"; + @path = ('ras'); + } + else { + print "$path[0]: no such directory exists\n"; + @path = @oldpath; + } + } + elsif($#path == 1) { + # mode and user + if($path[0] eq "sso") { + $user = select_sso_user($path[1]); + if($user eq "---") { + print "$path[1]: no such file exists in $pmode\n"; + @path = @oldpath; + } + else { + $mode = "SSO"; + @path = ('sso', $user); + } + } + elsif($path[0] eq "ras") { + $user = select_ras_user($path[1]); + if($user eq "---") { + print "$path[1]: no such file exists in $pmode\n"; + @path = @oldpath; + } + else { + $mode = "RAS"; + @path = ('ras', $user); + } + } + else { + print "$path[0]: no such directory exists\n"; + @path = @oldpath; + } + } + else { + if($cmd[1] eq '/') { + $mode = '---'; + $user = '---'; + @path = (); + } + else { + print "cd: invalid path specification\n"; + @path = @oldpath; + } + } + } + else { + if($#oldpath == 1) { + print "invalid path specification\n"; + @path = @oldpath; + } + else { + if($#path == 0) { + if($pmode eq "sso") { + $user = select_sso_user($path[0]); + if($user eq "---") { + print "$path[0]: no such file exists in $pmode\n"; + @path = @oldpath; + } + else { + $mode = "SSO"; + @path = ('sso', $user); + } + } + elsif($pmode eq "ras") { + $user = select_ras_user($path[0]); + if($user eq "---") { + print "$path[0]: no such file exists in $pmode\n"; + @path = @oldpath; + } + else { + $mode = "SSO"; + @path = ('sso', $user); + } + } + else { + print "$path[0]: no such directory exists\n"; + @path = @oldpath; + } + } + else { + print "invalid path specification\n"; + @path = @oldpath; + } + } + } + } # if .. } elsif ($cmd[0] eq "im") { if($mode eq "RAS") { @@ -353,11 +447,11 @@ sub prompt { send_im($user, $msgbody); } else { - print "?\n"; + print "im: no user selected\n"; } } else { - print "?\n"; + print "im: invalid command outside of ras directory\n"; } } elsif ($cmd[0] eq "trace") { @@ -369,97 +463,133 @@ sub prompt { print_sso_user; } else { - print "?\n"; + print "trace: no user select\n"; } } else { - print "?\n"; + print "trace: invalid command outside of ras directory\n"; } } elsif ($cmd[0] eq "field") { - if($user ne "---") { - my $act = $account->{account}; - print "$user\-\>$cmd[1]: $act->{$cmd[1]}\n"; + if($mode eq "SSO") { + if($user ne "---") { + my $act = $account->{account}; + print "$user\-\>$cmd[1]: $act->{$cmd[1]}\n"; + } + else { + print "field: no user selected\n"; + } } else { - print "?\n"; + print "field: command invalid outside of ras directory\n"; } } - elsif ($cmd[0] eq "describe") { - if($user ne "---") { - if($mode eq "SSO") { - print_sso_user; - } - elsif($mode eq "RAS") { - print_ras_user($user); - } + elsif ($cmd[0] eq "sessions") { + if($mode ne "RAS") { + print "sessions: must be in ras mode\n"; } else { - print "?\n"; + ls "active RAS sessions", list_ras_sessions(); } } - elsif ($cmd[0] eq "list") { - if($cmd[1] eq "users") { - if($mode eq "SSO") { - list_sso_users(); - } - elsif($mode eq "RAS") { - list_ras_users(); - } + elsif ($cmd[0] eq "lssn") { + if($mode ne "SSO" || $user eq "---") { + print "lssn: must be in sso mode with a user selected\n"; + } + else { + my @sns = list_ras_screennames($user); + ls "RAS screen names for $user", @sns; + } + } + elsif ($cmd[0] eq "ls") { + my @entries = (); + + if($mode eq "---") { + @entries = list_directories(); } - elsif($cmd[1] eq "sessions") { - if($mode eq "RAS") { - list_ras_sessions(); + else { + if($user ne "---") { + if($mode eq "SSO") { + print_sso_user; + } + elsif($mode eq "RAS") { + print_ras_user($user); + } } else { - print "?\n"; - } - } - elsif($cmd[1] eq "sn") { - if($mode eq "SSO") { - if($user ne "---") { - list_ras_screennames $user; + if($mode eq "SSO") { + @entries = list_sso_users(); } else { - print "?\n"; + @entries = list_ras_users(); } } - else { - print "?\n"; - } } - else { - print "?\n"; + + if(@entries) { + my $col = 0; + my $pstr = join('/', @path); + my $pfin = "/$pstr"; + ls $pfin, @entries; } + } else { - print "?\n" + print "$cmd[0]: command not found\n"; } } } sub main { - GetOptions("rasurl=s" => \$rasurl, - "apikey=s" => \$apikey) - or die("error in command line arguments"); + ($wchar, $hchar, $wpixels, $hpixels) = GetTerminalSize(); - ($wchar, $hchar, $wpixels, $hpixels) = GetTerminalSize(); + GetOptions("site=s" => \$site) or die "error in command line arguments"; + + if($site eq "") { + print "modcon: must supply -site command-line argument\n"; + return; + } $cnclient = REST::Client->new({ - host => 'https://chivanet.org/rest/api', + host => "https://$site/rest/api", timeout => 10}); - - $cnclient->addHeader('Authorization', "Apikey $apikey"); - - $rasclient = REST::Client->new({ - host => $rasurl, - timeout => 10}); print "ChivaNet MODCON $modcon_version\n"; print " Copyright (C) 2025 Coherent Logic Development LLC\n\n"; + + print "username: "; + $sso_account = ; + chomp($sso_account); + + print "password: "; + ReadMode('noecho'); + my $password = ; + chomp($password); + ReadMode('normal'); + + + my $params = $cnclient->buildQuery([username => $sso_account, password => $password]); + my $result = $cnclient->POST("/chivanet/modcon_auth", substr($params, 1), {'Content-type' => 'application/x-www-form-urlencoded'}); + my $http_response = $result->{_res}; + my $json = $http_response->{_content}; + my $apiresult = decode_json($json); - prompt(); + if($apiresult->{ok} == 1) { + $cnclient->addHeader('Authorization', "Apikey $apiresult->{token}"); + + select_sso_user $sso_account; + my $act = $account->{account}; + + print "\n\nWelcome to MODCON, $act->{display_name}!\n\n"; + + prompt(); + } + else { + print "\nerror: $apiresult->{error}\n"; + return; + } + print "Goodbye.\n" }