Annotation of ChivanetModcon/modcon, revision 1.5
1.1 snw 1: #!/usr/bin/env perl
2:
3: #
4: # ChivaNet Moderator Console
5: # Copyright (C) 2025 Coherent Logic Development LLC
6: #
1.5 ! snw 7: # $Id: modcon,v 1.4 2025/01/31 19:41:00 snw Exp $
1.1 snw 8: #
9: # Author: Serena Willis <snw@coherent-logic.com>
10: #
11: # Licensed AGPL-3.0
12: #
1.2 snw 13: # $Log: modcon,v $
1.5 ! snw 14: # Revision 1.4 2025/01/31 19:41:00 snw
! 15: # Move to a UNIX UI paradigm
! 16: #
1.4 snw 17: # Revision 1.3 2025/01/31 15:39:06 snw
18: # Minor fixes
19: #
1.3 snw 20: # Revision 1.2 2025/01/31 13:38:51 snw
21: # Initial basic functions working
22: #
1.2 snw 23: # Revision 1.1.1.1 2025/01/30 19:16:06 snw
24: # Initial commit
25: #
1.1 snw 26: #
27:
28: use REST::Client;
29: use JSON;
30: use Data::Dumper;
31: use Term::ReadKey;
1.4 snw 32: use Getopt::Long;
1.1 snw 33:
1.4 snw 34: my $sso_account = '';
1.1 snw 35: my $apikey = '';
36: my $online = 0;
37: my $modcon_version = '0.0.1';
38: my $cnclient = '';
39: my $wchar = '';
40: my $hchar = '';
41: my $wpixels = '';
42: my $hpixels = '';
1.2 snw 43: my $account = '';
1.4 snw 44: my $mode = '---';
45: my $user = '---';
46: $site = '';
47:
48: sub list_directories {
49: return ('sso', 'ras');
50: }
1.2 snw 51:
1.1 snw 52: sub list_sso_users {
1.4 snw 53: $cnclient->GET("/chivanet/users");
1.1 snw 54: my $json = $cnclient->responseContent();
55: my $hashref = decode_json($json);
56:
57: if($hashref->{ok} == 1) {
58: my $arrayref = $hashref->{users};
1.4 snw 59: my @users = @{$arrayref};
60:
61: return @users;
1.1 snw 62: }
63: else {
64: print "RPC error\n";
1.4 snw 65: return ();
1.1 snw 66: }
67: }
68:
69:
1.2 snw 70: sub select_sso_user {
71: my($id) = @_;
72:
73: $cnclient->GET("/chivanet/validate_user?id=$id");
74: my $json = $cnclient->responseContent();
75: my $hashref = decode_json($json);
76:
77: if($hashref->{exists} == 1) {
78:
79: $cnclient->GET("/chivanet/user?id=$id");
80: my $json = $cnclient->responseContent();
81: $account = decode_json($json);
1.4 snw 82:
1.2 snw 83: return $id;
84: }
85: else {
86: return "---";
1.3 snw 87: }
1.2 snw 88: }
89:
90: sub select_ras_user {
91: my($id) = @_;
92:
93: $cnclient->GET("/chivanet/validate_sn?id=$id");
94: my $json = $cnclient->responseContent();
95: my $hashref = decode_json($json);
96:
97: if($hashref->{exists} == 1) {
98: return $id;
99: }
100: else {
101: return "---";
102: }
103: }
104:
105: sub trace_ras_sn {
106: my($user) = @_;
107:
108: $cnclient->GET("/chivanet/trace_sn?id=$user");
109: my $json = $cnclient->responseContent();
110: my $hashref = decode_json($json);
111: my $record = $hashref->{account};
112: my $result = $record->{id};
113:
114: print "RAS screen name $user belongs to SSO account $result; switching to SSO mode\n";
1.3 snw 115: return $result;
1.2 snw 116: }
117:
118: sub list_ras_sessions {
119: $cnclient->GET("/chivanet/ras_sessions");
120: my $json = $cnclient->responseContent();
121: my $hashref = decode_json($json);
122: my $sessions = $hashref->{sessions};
123: my $arrayref = $sessions->{sessions};
1.5 ! snw 124: my @result = ();
1.2 snw 125:
126: foreach my $session (@{$arrayref}) {
1.5 ! snw 127: push(@result, $session->{id});
1.2 snw 128: }
129:
1.5 ! snw 130: return @result;
1.2 snw 131: }
132:
133: sub list_ras_screennames {
134: my($id) = @_;
1.5 ! snw 135:
! 136: my @result = ();
! 137: $cnclient->GET("/chivanet/user_ras_screen_names?id=$id");
1.2 snw 138: my $json = $cnclient->responseContent();
139: my $hashref = decode_json($json);
140: my $arrayref = $hashref->{screen_names};
1.5 ! snw 141:
! 142: foreach my $item (@{$arrayref}) {
! 143: push(@result, $item->{screen_name});
! 144: }
! 145:
1.2 snw 146:
1.5 ! snw 147: return @result;
1.4 snw 148: }
1.2 snw 149:
1.4 snw 150: sub list_ras_users {
1.3 snw 151:
1.5 ! snw 152: my @result = ();
1.4 snw 153:
1.5 ! snw 154: $cnclient->GET("/chivanet/all_ras_screen_names");
! 155: my $json = $cnclient->responseContent();
! 156: my $hashref = decode_json($json);
! 157:
! 158: if($hashref->{ok} == 0) {
! 159: print "RPC error\n";
! 160: return @result;
! 161: }
! 162:
! 163: my $arrayref = $hashref->{screen_names};
! 164:
! 165: foreach my $entryref (@{$arrayref}) {
! 166: push(@result, $entryref->{id});
! 167: }
! 168:
! 169: return @result;
1.2 snw 170: }
171:
172: sub print_sso_user {
173: my $act = $account->{account};
174:
175: print "\n";
176: print "Username : $act->{id}\n";
177: print "Real Name : $act->{last_name}, $act->{first_name}\n";
178: print "Display Name : $act->{display_name}\n";
179: print "Pronouns : $act->{pronouns}\n";
180: print "Profile Image : https://chivanet.org$act->{profile_photo}\n";
181: print "E-Mail Address : $act->{email}\n";
182: print "Permission Level : $act->{perm_level}\n";
183: print "Created : $act->{create_ts}\n";
184: print "Banned : $act->{mod_banned}\n\n";
185: }
186:
187: sub print_ras_user {
188: my($id) = @_;
189:
190: $cnclient->GET("/chivanet/sn_status?id=$id");
191: my $json = $cnclient->responseContent();
192: my $hashref = decode_json($json);
193: my $status = $hashref->{status};
194: my $online = "offline";
195:
196: if($status->{online} == 1) {
197: $online = "online";
198: }
199:
200: print "\n";
201:
202:
203: if($online eq "online") {
204: my $sess = $status->{session};
205: my $service = 'AIM';
206:
207: if($sess->{is_icq} == 1) {
208: $service = 'ICQ';
209: }
210:
211: if($sess->{idle_seconds} > 0) {
212: $online = "idle";
213: }
214:
215: print "Screen Name : $id [$online]\n";
216: print "Service : $service\n";
217: if($online eq "online") {
218: print "Time Online (secs): $sess->{online_seconds}\n";
219: }
220: else {
221: print "Time Idle (secs) : $sess->{idle_seconds}\n";
222: }
223: print "Away Message : $sess->{away_message}\n\n";
224: }
225: else {
226: print "Screen Name : $id [$online]\n\n";
1.5 ! snw 227: }
1.2 snw 228:
229: }
230:
231: sub send_im {
232: my($user, $msgbody) = @_;
233:
234: print "sending message \"$msgbody\" to $user...";
235:
236: my $msg = "<font color=red><strong>[ChivaNet Support]:</strong></font> $msgbody";
237:
238: $cnclient->GET("/chivanet/send_im?from=ChivaNet&to=$user&message=$msg");
239: my $json = $cnclient->responseContent();
240: my $hashref = decode_json($json);
241:
242: if($hashref->{status} == 1) {
243: print "[OK]\n";
244: }
245: else {
246: print "[FAIL]\n";
247: }
248: }
249:
1.5 ! snw 250: sub ls {
! 251: my ($description, @entries) = @_;
! 252:
! 253: my @sorted = sort(@entries);
! 254: my $maxlen = 0;
! 255: foreach my $entry (@sorted) {
! 256: my $len = length($entry);
! 257: if ($len > $maxlen) {
! 258: $maxlen = $len;
! 259: }
! 260: }
! 261: $maxlen = $maxlen + 2;
! 262: my $ct = $#sorted + 1;
! 263: foreach my $entry (@sorted) {
! 264: if($col + $maxlen >= $wchar) {
! 265: print "\n";
! 266: $col = 0;
! 267: }
! 268: else {
! 269: printf("%-$maxlen\s", $entry);
! 270: $col = $col + $maxlen;
! 271: }
! 272: }
! 273: print "\n$ct items in $description\n";
! 274:
! 275: }
! 276:
1.1 snw 277: sub prompt {
278:
279: my $rawcmd = '';
1.4 snw 280: my $pmode = $mode;
281: my @path = ();
282:
1.1 snw 283: while (1) {
1.4 snw 284: $pmode = lc $mode;
285: my $prompt = '';
286: if($pmode ne "---") {
287: if($user eq '---') {
288: $prompt = "$sso_account\@$site:[/$pmode]\$ ";
289: }
290: else {
291: $prompt = "$sso_account\@$site:[/$pmode/$user]\$ ";
292: }
293: }
294: else {
295: $prompt = "$sso_account\@$site:[/]\$ ";
296: }
297: print $prompt;
1.1 snw 298: my $line = <STDIN>;
299: chomp($line);
300: $rawcmd = $line;
301:
302: my @cmd = split(' ', $rawcmd);
303:
1.5 ! snw 304: if ($cmd[0] eq "exit" || $cmd[0] eq "logout" || $cmd[0] eq "quit" || $cmd[0] eq "bye" || $cmd[0] eq "lo") {
1.1 snw 305: return;
306: }
1.4 snw 307: elsif ($cmd[0] eq "pwd") {
308: my $pstr = join('/', @path);
309: print "/$pstr\n";
310: }
311: elsif ($cmd[0] eq "cd") {
312: my $abspath = false;
313:
314: if($cmd[1] eq "..") {
315: if(@path) {
316: if($#path == 1) {
317: $user = "---";
318: @path = ($pmode);
319: }
320: elsif($#path == 0) {
321: $mode = "---";
322: @path = ();
323: }
324: }
325: else {
326: print "already in root directory\n";
327: }
1.2 snw 328: }
329: else {
1.4 snw 330: my @oldpath = @path;
331: @path = split('/', $cmd[1]);
332:
333: if(substr($cmd[1], 0, 1) eq '/') {
334: $abspath = true;
335: shift @path;
336: }
337:
338: if($abspath eq true || !@oldpath) {
339: if($#path == 0) {
340: # mode, no user
341: if($path[0] eq "sso") {
342: $mode = "SSO";
343: $user = "---";
344: @path = ('sso');
345: }
346: elsif($path[0] eq "ras") {
347: $mode = "RAS";
348: $user = "---";
349: @path = ('ras');
350: }
351: else {
352: print "$path[0]: no such directory exists\n";
353: @path = @oldpath;
354: }
355: }
356: elsif($#path == 1) {
357: # mode and user
358: if($path[0] eq "sso") {
359: $user = select_sso_user($path[1]);
360: if($user eq "---") {
361: print "$path[1]: no such file exists in $pmode\n";
362: @path = @oldpath;
363: }
364: else {
365: $mode = "SSO";
366: @path = ('sso', $user);
367: }
368: }
369: elsif($path[0] eq "ras") {
370: $user = select_ras_user($path[1]);
371: if($user eq "---") {
372: print "$path[1]: no such file exists in $pmode\n";
373: @path = @oldpath;
374: }
375: else {
376: $mode = "RAS";
377: @path = ('ras', $user);
378: }
379: }
380: else {
381: print "$path[0]: no such directory exists\n";
382: @path = @oldpath;
383: }
384: }
385: else {
386: if($cmd[1] eq '/') {
387: $mode = '---';
388: $user = '---';
389: @path = ();
390: }
391: else {
392: print "cd: invalid path specification\n";
393: @path = @oldpath;
394: }
395: }
1.2 snw 396: }
397: else {
1.4 snw 398: if($#oldpath == 1) {
399: print "invalid path specification\n";
400: @path = @oldpath;
401: }
402: else {
403: if($#path == 0) {
404: if($pmode eq "sso") {
405: $user = select_sso_user($path[0]);
406: if($user eq "---") {
407: print "$path[0]: no such file exists in $pmode\n";
408: @path = @oldpath;
409: }
410: else {
411: $mode = "SSO";
412: @path = ('sso', $user);
413: }
414: }
415: elsif($pmode eq "ras") {
416: $user = select_ras_user($path[0]);
417: if($user eq "---") {
418: print "$path[0]: no such file exists in $pmode\n";
419: @path = @oldpath;
420: }
421: else {
422: $mode = "SSO";
423: @path = ('sso', $user);
424: }
425: }
426: else {
427: print "$path[0]: no such directory exists\n";
428: @path = @oldpath;
429: }
430: }
431: else {
432: print "invalid path specification\n";
433: @path = @oldpath;
434: }
435: }
436: }
1.5 ! snw 437: } # if ..
1.2 snw 438: }
439: elsif ($cmd[0] eq "im") {
440: if($mode eq "RAS") {
441: if($user ne "---") {
442: my @msga = @cmd[1..$#cmd];
443: my $msgbody = join(' ', @msga);
444: send_im($user, $msgbody);
445: }
446: else {
1.4 snw 447: print "im: no user selected\n";
1.2 snw 448: }
449: }
450: else {
1.4 snw 451: print "im: invalid command outside of ras directory\n";
1.2 snw 452: }
453: }
454: elsif ($cmd[0] eq "trace") {
455: if($mode eq "RAS") {
456: if($user ne "---") {
457: my $id = trace_ras_sn($user);
458: $mode = "SSO";
459: $user = select_sso_user($id);
460: print_sso_user;
461: }
462: else {
1.4 snw 463: print "trace: no user select\n";
1.2 snw 464: }
465: }
466: else {
1.4 snw 467: print "trace: invalid command outside of ras directory\n";
1.2 snw 468: }
469: }
470: elsif ($cmd[0] eq "field") {
1.4 snw 471: if($mode eq "SSO") {
472: if($user ne "---") {
473: my $act = $account->{account};
474: print "$user\-\>$cmd[1]: $act->{$cmd[1]}\n";
475: }
476: else {
477: print "field: no user selected\n";
478: }
1.2 snw 479: }
480: else {
1.4 snw 481: print "field: command invalid outside of ras directory\n";
1.2 snw 482: }
483: }
1.5 ! snw 484: elsif ($cmd[0] eq "sessions") {
! 485: if($mode ne "RAS") {
! 486: print "sessions: must be in ras mode\n";
! 487: }
! 488: else {
! 489: ls "active RAS sessions", list_ras_sessions();
! 490: }
! 491: }
! 492: elsif ($cmd[0] eq "lssn") {
! 493: if($mode ne "SSO" || $user eq "---") {
! 494: print "lssn: must be in sso mode with a user selected\n";
! 495: }
! 496: else {
! 497: my @sns = list_ras_screennames($user);
! 498: ls "RAS screen names for $user", @sns;
! 499: }
! 500: }
1.4 snw 501: elsif ($cmd[0] eq "ls") {
502: my @entries = ();
503:
504: if($mode eq "---") {
505: @entries = list_directories();
1.1 snw 506: }
507: else {
1.4 snw 508: if($user ne "---") {
509: if($mode eq "SSO") {
510: print_sso_user;
511: }
512: elsif($mode eq "RAS") {
513: print_ras_user($user);
514: }
1.2 snw 515: }
516: else {
1.4 snw 517: if($mode eq "SSO") {
518: @entries = list_sso_users();
1.2 snw 519: }
520: else {
1.4 snw 521: @entries = list_ras_users();
1.2 snw 522: }
523: }
1.4 snw 524: }
525:
526: if(@entries) {
527: my $col = 0;
528: my $pstr = join('/', @path);
529: my $pfin = "/$pstr";
1.5 ! snw 530: ls $pfin, @entries;
1.2 snw 531: }
1.4 snw 532:
1.1 snw 533: }
534: else {
1.4 snw 535: print "$cmd[0]: command not found\n";
1.1 snw 536: }
537: }
538:
539: }
540:
541: sub main {
1.4 snw 542: ($wchar, $hchar, $wpixels, $hpixels) = GetTerminalSize();
1.1 snw 543:
1.4 snw 544: GetOptions("site=s" => \$site) or die "error in command line arguments";
545:
546: if($site eq "") {
547: print "modcon: must supply -site command-line argument\n";
548: return;
549: }
1.1 snw 550:
551: $cnclient = REST::Client->new({
1.4 snw 552: host => "https://$site/rest/api",
1.1 snw 553: timeout => 10});
554:
555: print "ChivaNet MODCON $modcon_version\n";
556: print " Copyright (C) 2025 Coherent Logic Development LLC\n\n";
1.4 snw 557:
558: print "username: ";
559: $sso_account = <STDIN>;
560: chomp($sso_account);
561:
562: print "password: ";
563: ReadMode('noecho');
564: my $password = <STDIN>;
565: chomp($password);
566: ReadMode('normal');
567:
568:
569: my $params = $cnclient->buildQuery([username => $sso_account, password => $password]);
570: my $result = $cnclient->POST("/chivanet/modcon_auth", substr($params, 1), {'Content-type' => 'application/x-www-form-urlencoded'});
571: my $http_response = $result->{_res};
572: my $json = $http_response->{_content};
573: my $apiresult = decode_json($json);
1.1 snw 574:
1.4 snw 575: if($apiresult->{ok} == 1) {
576: $cnclient->addHeader('Authorization', "Apikey $apiresult->{token}");
577:
578: select_sso_user $sso_account;
579: my $act = $account->{account};
580:
581: print "\n\nWelcome to MODCON, $act->{display_name}!\n\n";
582:
583: prompt();
584: }
585: else {
586: print "\nerror: $apiresult->{error}\n";
587: return;
588: }
589:
1.1 snw 590: print "Goodbye.\n"
591: }
592:
593: main();
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>