--- ChivanetModcon/modcon 2025/01/31 15:39:06 1.3 +++ ChivanetModcon/modcon 2025/03/10 15:31:54 1.7 @@ -4,13 +4,26 @@ # ChivaNet Moderator Console # Copyright (C) 2025 Coherent Logic Development LLC # -# $Id: modcon,v 1.3 2025/01/31 15:39:06 snw Exp $ +# $Id: modcon,v 1.7 2025/03/10 15:31:54 snw Exp $ # # Author: Serena Willis # # Licensed AGPL-3.0 # +# # $Log: modcon,v $ +# Revision 1.7 2025/03/10 15:31:54 snw +# Test +# +# Revision 1.6 2025/02/04 18:55:12 snw +# Updates +# +# 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 # @@ -24,59 +37,46 @@ use REST::Client; use JSON; -use Getopt::Long; use Data::Dumper; use Term::ReadKey; +use Getopt::Long; +use Text::Glob qw(match_glob); -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) = @_; @@ -90,11 +90,10 @@ sub select_sso_user { $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 "---"; } } @@ -110,7 +109,6 @@ sub select_ras_user { return $id; } else { - print ">>> invalid RAS screen name $id\n"; return "---"; } } @@ -134,85 +132,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; - my $total = 0; - print "\n"; + 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; - $total = $total + 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; - } + if($hashref->{ok} == 0) { + print "RPC error\n"; + return @result; } + + my $arrayref = $hashref->{screen_names}; - print "\nSSO user $id has $total screen names\n\n"; + foreach my $entryref (@{$arrayref}) { + push(@result, $entryref->{id}); + } + return @result; } sub print_sso_user { @@ -270,9 +235,7 @@ sub print_ras_user { } else { print "Screen Name : $id [$online]\n\n"; - } - -# print Dumper($status); + } } @@ -295,57 +258,208 @@ sub send_im { } } +sub ls { + my ($description, $match, @entries) = @_; + + if($match eq "") { + $match = '*'; + } + + print "Directory of [$description] matching \'$match\':\n\n"; + + 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(match_glob($match, $entry)) { + if($col + $maxlen >= $wchar) { + print "$entry\n"; + $col = 0; + } + else { + printf("%-$maxlen\s", $entry); + $col = $col + $maxlen; + } + } + else { + $ct = $ct - 1; + } + } + + print "\n\n$ct matching items in directory\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 = "RAS"; + @path = ('ras', $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") { @@ -355,11 +469,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") { @@ -371,97 +485,148 @@ 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"; - } - } - elsif ($cmd[0] eq "list") { - if($cmd[1] eq "users") { - if($mode eq "SSO") { - list_sso_users(); + if($cmd[1]) { + ls "active RAS sessions", $cmd[1], list_ras_sessions(); } - elsif($mode eq "RAS") { - list_ras_users(); + else { + ls "active RAS sessions", '*', list_ras_sessions(); } } - elsif($cmd[1] eq "sessions") { - if($mode eq "RAS") { - list_ras_sessions(); + } + 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); + if($cmd[1]) { + ls "RAS screen names for $user", $match, @sns; } else { - print "?\n"; + ls "RAS screen names for $user", '*', @sns; } + } + } + elsif ($cmd[0] eq "ls") { + my @entries = (); + + if($mode eq "---") { + @entries = list_directories(); } - elsif($cmd[1] eq "sn") { - if($mode eq "SSO") { - if($user ne "---") { - list_ras_screennames $user; + else { + if($user ne "---") { + if($mode eq "SSO") { + print_sso_user; + } + elsif($mode eq "RAS") { + print_ras_user($user); + } + } + else { + if($mode eq "SSO") { + @entries = list_sso_users(); } else { - print "?\n"; + @entries = list_ras_users(); } } + } + + if(@entries) { + my $col = 0; + my $pstr = join('/', @path); + my $pfin = "/$pstr"; + if($cmd[1]) { + ls $pfin, $cmd[1], @entries; + } else { - print "?\n"; + ls $pfin, '*', @entries; } } - else { - print "?\n"; - } + } 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" }