From b33e4fe5e1ce2eb833af2817d07c5324e9ada061 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 25 Jul 2025 13:46:16 -0400 Subject: [PATCH] visibility command added, a few clean up and bug fixes. --- ttyverse.pl | 240 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 173 insertions(+), 67 deletions(-) diff --git a/ttyverse.pl b/ttyverse.pl index 2a89353..67ec175 100755 --- a/ttyverse.pl +++ b/ttyverse.pl @@ -755,6 +755,7 @@ $lat ||= undef; $long ||= undef; $location ||= 0; $linelength ||= 5000; # Generous default, will be updated from server +$post_visibility ||= "public"; # Default post visibility: public, unlisted, private, direct # Fediverse server configuration - defaults to mastodon.social $fediverseserver ||= "mastodon.social"; $oauthbase ||= $apibase || "${http_proto}://${fediverseserver}"; @@ -2503,82 +2504,97 @@ print $stdout "*** invalid UTF-8: partial delete of a wide character?\n"; if ($_ eq '/help' || $_ eq '/?') { print <<'EOF'; - *** BASIC COMMANDS: :a$AAOOOOOOOOOOOOOOOOOAA$a, ================== - +@A:. .:B@+ ANYTHING WITHOUT - /refresh =@B HELP!!! HELP!!! B@= A LEADING / IS - grabs the newest :a$Ao oA$a, SENT AS A TWEET! - tweets right ;AAA$a; :a$AAAAAAAAAAA; ================== - away (or tells :AOaaao:, .:oA*:. JUST TYPE TO TALK! - you if there .;=$$$OBO***+ .+aaaa$: - is nothing new) :*; :***O@Aaaa*o, ============ - by thumping .+++++: o#o REMEMBER!! - the background :OOOOOOA*:::, =@o ,:::::. ============ - process. .+++++++++: =@*.....=a$OOOB#; MANY COMMANDS, AND - =@OoO@BAAA#@$o, ALL TWEETS ARE - /again =@o .+aaaaa: --ASYNCHRONOUS-- - displays most recent =@Aaaaaaaaaa*o*a;, and might not always - tweets, both old and =@$++=++++++:,;+aA: respond - new. ,+$@*.=O+ ...oO; oAo+. immediately! - ,+o$OO=.+aA#####Oa;.*OO$o+. - /dm and /dmagain for DMs. +Ba::;oaa*$Aa=aA$*aa=;::$B: - ,===O@BOOOOOOOOO#@$===, - /replies o@BOOOOOOOOO#@+ ================== - shows replies and mentions. o@BOB@B$B@BO#@+ USE + FOR A COUNT: - o@*.a@o a@o.$@+ /re +30 => last 30 replies - /quit resumes your boring life. o@B$B@o a@A$#@+ ========================== +*** BASIC COMMANDS *** + +IMPORTANT: Anything without a leading / is sent as a post! +Just type to talk! + +/refresh + Grabs the newest posts right away (or tells you if there + is nothing new) by checking the background process. + +/again + Displays most recent posts, both old and new. + +/dm and /dmagain + For direct messages. + +/replies + Shows replies and mentions. + +/timelines + Lists available timelines to view. + +/timeline + Switch to timeline (home, public, federated, notifications, etc.) + +/visibility [level] + Show current post visibility or set to: public, unlisted, private, direct + +/quit + Resumes your boring life. + +REMEMBER: Many commands and all posts are ASYNCHRONOUS and might +not always respond immediately! + +USE + FOR A COUNT: /re +30 => last 30 replies + EOF &linein("PRESS RETURN/ENTER>"); print <<"EOF"; -+- MORE COMMANDS -+ -=-=- USER STUFF -=-=- -| | /whois username displays info about username -| See the TTYverse | /again username views their most recent tweets -| home page for | /wagain username combines them all -| complete list | /follow username follow a username -| | /leave username stop following a username -+-----------------+ /dm username message send a username a DM -+--- TWEET AND DM SELECTION -------------------------------------------------+ -| all DMs and tweets have menu codes (letters + number, d for DMs). example: | -| a5> Send me Dr Pepper http://www.floodgap.com/TTYverse | -| [DM da0][ttytter/Sun Jan 32 1969] I think you are cute | -| /reply a5 message replies to tweet a5 | -| example: /reply a5 I also like Dr Pepper | -| becomes \@ttytter I also like Dr Pepper (and is threaded) | -| /thread a5 if a5 is part of a thread (the username | -| has a \@) then show all posts up to that | -| /url a5 opens all URLs in post a5 | -| Mac OS X users, do first: /set urlopen open %U | -| Dummy terminal users, try /set urlopen lynx -dump %U | more | -| /delete a5 deletes post a5, if it's your post | -| /boost a5 boosts post a5 | -+-- Abbreviations: /re, /th, /url, /del --- menu codes wrap around at end ---+ -=====> /reply, /delete and /url work for direct message menu codes too! <===== +*** MORE COMMANDS *** + +USER COMMANDS: + /whois username - displays info about username + /again username - views their most recent posts + /wagain username - combines them all + /follow username - follow a username + /leave username - stop following a username + /dm username message - send a username a DM + +POST AND DM SELECTION: +All DMs and posts have menu codes (letters + number, d for DMs). +Example: + a5> Send me Dr Pepper http://example.com + [DM da0][user/Sun Jan 32 1969] I think you are cute + + /reply a5 message - replies to post a5 + /thread a5 - show all posts in thread if a5 is threaded + /url a5 - opens all URLs in post a5 + /delete a5 - deletes post a5, if it's your post + /boost a5 - boosts post a5 + +Abbreviations: /re, /th, /url, /del +Menu codes wrap around at end. + +Note: /reply, /delete and /url work for direct message menu codes too! + EOF &linein("PRESS RETURN/ENTER>"); print <<"EOF"; +*** CONFIGURATION *** +Use /set to turn on options or set them at runtime. -Use /set to turn on options or set them at runtime. There is a BIG LIST! +EXAMPLES: + /set ansi 1 - Enable ANSI colors (or use -ansi command line option) + /set verify 1 - Verify posts before posting (or use -verify option) ->> EXAMPLE: WANT ANSI? /set ansi 1 - or use the -ansi command line option. - WANT TO VERIFY YOUR POSTS BEFORE POSTING? /set verify 1 - or use the -verify command line option. -For more, like readline support, UTF-8, SSL, proxies, etc., see the docs. +For more options like readline support, UTF-8, SSL, proxies, etc., +see the documentation. -** READ THE COMPLETE DOCUMENTATION: http://www.floodgap.com/software/ttytter/ +*** ABOUT TTYverse *** - TTYverse $TTYverse_VERSION is (c)2012 cameron kaiser + contributors. - all rights reserved. this software is offered AS IS, with no guarantees. it - is not endorsed by any fediverse server operators or developers. - - *** TTYverse: fediverse client derived from TTYtter - original TTYtter by cameron kaiser - http://www.floodgap.com/software/ttytter/ - send your suggestions to me at ckaiser\@floodgap.com +TTYverse $TTYverse_VERSION - Fediverse client derived from TTYtter +Original TTYtter by Cameron Kaiser +This software is offered AS IS, with no guarantees. +It is not endorsed by any fediverse server operators or developers. +Documentation: http://www.floodgap.com/software/ttytter/ +Suggestions: mention \@stormux\@social.stormux.org EOF return 0; @@ -3335,7 +3351,7 @@ m#^/(un)?f(boost|a|av|ave|avorite|avourite)? ([zZ]?[a-zA-Z]?[0-9]+)$#) { print $stdout "-- no such post (yet?): $code\n"; return 0; } - my $target = &descape($tweet->{'user'}->{'screen_name'}); + my $target = &descape($tweet->{'user'}->{'acct'} || $tweet->{'user'}->{'screen_name'}); $_ = '@' . $target . " $_"; unless ($mode eq 'v') { $in_reply_to = $tweet->{'id_str'}; @@ -3362,7 +3378,7 @@ m#^/(un)?f(boost|a|av|ave|avorite|avourite)? ([zZ]?[a-zA-Z]?[0-9]+)$#) { return 0; } # in the future, add DM in_reply_to here - my $target = &descape($dm->{'sender'}->{'screen_name'}); + my $target = &descape($dm->{'sender'}->{'acct'} || $dm->{'sender'}->{'screen_name'}); $readline_completion{'@'.lc($target)}++ if ($termrl); $_ = "/dm $target $_"; print $stdout &wwrap("(expanded to \"$_\")"); @@ -3433,6 +3449,93 @@ m#^/(un)?f(boost|a|av|ave|avorite|avourite)? ([zZ]?[a-zA-Z]?[0-9]+)$#) { return 0; } + # Timeline management commands + if ($_ eq '/timelines') { + print $stdout <<"EOF"; +Available timelines: + /timeline home - Your main feed (default) + /timeline public - Local public timeline + /timeline federated - Federated public timeline + /timeline notifications - Your notifications (mentions, boosts, etc.) + /timeline bookmarks - Your bookmarked posts + /timeline favourites - Your favourite posts + +Current timeline: home +EOF + return 0; + } + + if (m#^/timeline\s+(\w+)(\s+\+\d+)?$#) { + my $timeline_name = lc($1); + my $countmaybe = $2; + $countmaybe =~ s/[^\d]//g if (length($countmaybe)); + $countmaybe += 0; + + my $timeline_url; + my $timeline_display_name; + + # Map timeline names to API endpoints + if ($timeline_name eq 'home') { + $timeline_url = "${apibase}/timelines/home"; + $timeline_display_name = "home feed"; + } elsif ($timeline_name eq 'public') { + $timeline_url = "${apibase}/timelines/public?local=true"; + $timeline_display_name = "local public timeline"; + } elsif ($timeline_name eq 'federated') { + $timeline_url = "${apibase}/timelines/public"; + $timeline_display_name = "federated timeline"; + } elsif ($timeline_name eq 'notifications') { + $timeline_url = "${apibase}/notifications"; + $timeline_display_name = "notifications"; + } elsif ($timeline_name eq 'bookmarks') { + $timeline_url = "${apibase}/bookmarks"; + $timeline_display_name = "bookmarks"; + } elsif ($timeline_name eq 'favourites' || $timeline_name eq 'favorites') { + $timeline_url = "${apibase}/favourites"; + $timeline_display_name = "favourites"; + } else { + print $stdout "-- unknown timeline: $timeline_name\n"; + print $stdout "-- use /timelines to see available timelines\n"; + return 0; + } + + print $stdout "-- fetching $timeline_display_name...\n" if ($verbose); + my $my_json_ref = &grabjson($timeline_url, 0, 0, $countmaybe || 20, undef, 1); + + if ($timeline_name eq 'notifications') { + # Notifications have a different structure, need special handling + &dt_tdisplay($my_json_ref, "notifications"); + } else { + &dt_tdisplay($my_json_ref, $timeline_name); + } + return 0; + } + + # Visibility management commands + if ($_ eq '/visibility') { + my @visibilities = ('public', 'unlisted', 'private', 'direct'); + print $stdout "Available post visibility levels:\n"; + foreach my $vis (@visibilities) { + my $marker = ($vis eq $post_visibility) ? "* " : " "; + print $stdout "${marker}${vis}\n"; + } + return 0; + } + + if (m#^/visibility\s+(\w+)$#) { + my $new_visibility = lc($1); + my @valid_visibilities = ('public', 'unlisted', 'private', 'direct'); + + if (grep { $_ eq $new_visibility } @valid_visibilities) { + $post_visibility = $new_visibility; + print $stdout "-- post visibility set to: $post_visibility\n"; + } else { + print $stdout "-- invalid visibility level: $new_visibility\n"; + print $stdout "-- valid options: " . join(', ', @valid_visibilities) . "\n"; + } + return 0; + } + # DMs if ($_ eq '/dm' || $_ eq '/dmrefresh' || $_ eq '/dmr') { &dmthump; @@ -5333,6 +5436,7 @@ sub updatest { } $i .= "${payload}=${urle}${user_name_dm}" unless ($rt_id); $i .= "id=$rt_id" if ($rt_id); + $i .= "visibility=${post_visibility}&" unless ($rt_id || length($user_name_dm)); $slowpost += 0; if ($slowpost && !$script && !$status && !$silent) { if($pid = open(SLOWPOST, '-|')) { # pause background so that it doesn't kill itself @@ -6141,7 +6245,7 @@ sub defaultautocompletion { '/wagain', '/whois', '/thump', '/dm', '/refresh', '/dmagain', '/set', '/help', '/reply', '/url', '/thread', '/retweet', '/replyall', - '/replies', '/ruler', '/exit', '/me', '/vcheck', + '/replies', '/timelines', '/timeline', '/visibility', '/ruler', '/exit', '/me', '/vcheck', '/oretweet', '/eretweet', '/fretweet', '/liston', '/listoff', '/dmsent', '/rtsof', '/rtson', '/rtsoff', '/lists', '/withlist', '/add', '/padd', '/push', @@ -6307,7 +6411,7 @@ sub get_tweet { kill $SIGUSR2, $child if ($child); print C "pipet $code ----------\n"; while(length($k) < 1024) { - sysread(W, $l, 1024); + read(W, $l, 1024); $k .= $l; } return undef if ($k !~ /[^\s]/); @@ -6356,7 +6460,7 @@ sub get_dm { kill $SIGUSR2, $child if ($child); # prime pipe print C "piped $code ----------\n"; # internally two alphanum, recall while(length($k) < 1024) { - sysread(W, $l, 1024); + read(W, $l, 1024); $k .= $l; } return undef if ($k !~ /[^\s]/); @@ -6386,7 +6490,7 @@ sub getbackgroundkey { "DEFAULT"; print C substr(unpack("${pack_magic}H*", $ref).$space_pad, 0, 1024); while(length($k) < 1024) { - sysread(W, $l, 1024); + read(W, $l, 1024); $k .= $l; } $k =~ s/[^0-9a-fA-F]//g; @@ -7644,6 +7748,7 @@ sub map_single_status { $user->{'screen_name'} = $user->{'username'} if exists($user->{'username'}); $user->{'name'} = $user->{'display_name'} if exists($user->{'display_name'}); $user->{'profile_image_url'} = $user->{'avatar'} if exists($user->{'avatar'}); + # Keep acct field for full handle (username@domain.com) } # Recursively map reblogged status @@ -7661,6 +7766,7 @@ sub map_user_object { $user->{'screen_name'} = $user->{'username'} if exists($user->{'username'}); $user->{'name'} = $user->{'display_name'} if exists($user->{'display_name'}); $user->{'profile_image_url'} = $user->{'avatar'} if exists($user->{'avatar'}); + # Keep acct field for full handle (username@domain.com) $user->{'followers_count'} = $user->{'followers_count'} if exists($user->{'followers_count'}); $user->{'friends_count'} = $user->{'following_count'} if exists($user->{'following_count'}); $user->{'statuses_count'} = $user->{'statuses_count'} if exists($user->{'statuses_count'});