diff --git a/ttyverse.pl b/ttyverse.pl index ca751ef..8bb746e 100755 --- a/ttyverse.pl +++ b/ttyverse.pl @@ -150,7 +150,7 @@ BEGIN { # notificationpause=0 # Notification refresh rate (0=use main pause) # === INTERACTION === -# mentions=0 # Show mentions in timeline +# mentions=0 # Legacy flag (mentions included automatically) # synch=0 # Synchronous mode (blocks on requests) # maxhist=19 # Command history size # hold=0 # Hold mode for piped input @@ -253,7 +253,7 @@ EOF apibase fediverseserver queryurl idurl delurl dmdelurl favsurl notificationurl markersurl favurl favdelurl followurl leaveurl - dmupdate credurl blockurl blockdelurl friendsurl + dmupdate credurl blockurl blockdelurl muteurl mutedelurl friendsurl modifyliurl adduliurl delliurl getliurl getlisurl getfliurl creliurl delliurl deluliurl crefliurl delfliurl getuliurl getufliurl dmsenturl reblogurl boostsbyurl dmidurl @@ -278,7 +278,7 @@ EOF urlopen cli_browser gui_browser delurl notrack dmdelurl favsurl favurl favdelurl slowpost notifies filter colourdefault followurl leaveurl dmupdate mentions backload - lat long location searchhits blockurl blockdelurl woeid + lat long location searchhits blockurl blockdelurl muteurl mutedelurl woeid nocounter linelength friendsurl followersurl lists modifyliurl adduliurl delliurl getliurl getlisurl getfliurl creliurl delliurl deluliurl crefliurl delfliurl atrendurl @@ -393,6 +393,8 @@ EOF $leaveurl = "${apibase}/accounts/%I/unfollow"; $blockurl = "${apibase}/accounts/%I/block"; $blockdelurl = "${apibase}/accounts/%I/unblock"; + $muteurl = "${apibase}/accounts/%I/mute"; + $mutedelurl = "${apibase}/accounts/%I/unmute"; $favurl = "${apibase}/statuses/%I/favourite"; $favdelurl = "${apibase}/statuses/%I/unfavourite"; $blockingurl = "${apibase}/accounts/relationships?id[]=%I"; @@ -1337,6 +1339,8 @@ $followurl ||= "${apibase}/accounts/%I/follow"; $leaveurl ||= "${apibase}/accounts/%I/unfollow"; $blockurl ||= "${apibase}/accounts/%I/block"; $blockdelurl ||= "${apibase}/accounts/%I/unblock"; +$muteurl ||= "${apibase}/accounts/%I/mute"; +$mutedelurl ||= "${apibase}/accounts/%I/unmute"; $friendsurl ||= "${apibase}/accounts/%I/following"; $followersurl ||= "${apibase}/accounts/%I/followers"; # Twitter friendships/update removed - fediverse uses individual follow/unfollow endpoints @@ -1495,17 +1499,8 @@ if (!$dostream || $authtype eq 'basic' || !$ssl || $script || $anonymous || $syn } else { print $stdout "-- Streaming API enabled\n"; - # streams change mentions behaviour; we get them automatically. - # warn the user if the current settings are suboptimal. - if ($mentions) { - if ($nostreamreplies) { - print $stdout -"** warning: -mentions and -nostreamreplies are very inefficient together\n"; - } else { - print $stdout -"** warning: -mentions not generally needed in Streaming mode\n"; - } - } + # In fediverse, mentions are included automatically in home timeline + # and handled via background notifications - no separate flag needed } } else { $dostream = 0; } # -status suppresses streaming if (!$dostream && $streamallreplies) { @@ -3175,6 +3170,12 @@ USER COMMANDS: /wagain username - combines them all /follow username - follow a username /leave username - stop following a username + /block username - block a username + /block d4 - block author of post d4 + /unblock username - unblock a username + /mute username - mute a username (also mutes notifications) + /mute d4 - mute author of post d4 + /unmute username - unmute a username /followers [username] - show who follows you (or username) /following [username] - show who you follow (or username follows) /dm username message - send a username a DM @@ -4469,24 +4470,91 @@ EOF return 0; } - # block and unblock users - if (m#^/(block|unblock) \@?([^\s/]+)$#) { + # block and unblock users (with menu code support) + if (m#^/(block|unblock)(?:\s+([a-z]+\d+)|\s+\@?([^\s/]+))$#) { my $m = $1; - my $u = lc($2); + my $code = $2; # menu code like 'd4' + my $username = $3; # username like '@user' + + my $target_user; + my $post_ref; + + if ($code) { + # Menu code provided - get username from timeline + $post_ref = $tl{$code}; + if (!$post_ref) { + print $stdout "-- sorry, I don't know that code.\n"; + return 0; + } + $target_user = lc($post_ref->{'user'}->{'acct'} || $post_ref->{'user'}->{'username'}); + if (!$target_user) { + print $stdout "-- sorry, couldn't get user from that post.\n"; + return 0; + } + } else { + # Username provided directly + $target_user = lc($username); + } + if ($m eq 'block') { - $answer = lc(&linein( - "-- sure you want to block $u? (only y or Y is affirmative):")); + my $confirm_msg = $code ? + "-- sure you want to block $target_user (author of $code)? (only y or Y is affirmative):" : + "-- sure you want to block $target_user? (only y or Y is affirmative):"; + $answer = lc(&linein($confirm_msg)); if ($answer ne 'y') { - print $stdout "-- ok, $u is NOT blocked.\n"; + print $stdout "-- ok, $target_user is NOT blocked.\n"; return 0; } } - &boruuser($u, 1, + &boruuser($target_user, 1, (($m eq 'block') ? $blockurl : $blockdelurl), (($m eq 'block') ? 'started' : 'stopped')); return 0; } + # mute and unmute users (with menu code support) + if (m#^/(mute|unmute)(?:\s+([a-z]+\d+)|\s+\@?([^\s/]+))$#) { + my $m = $1; + my $code = $2; # menu code like 'd4' + my $username = $3; # username like '@user' + + my $target_user; + my $post_ref; + + if ($code) { + # Menu code provided - get username from timeline + $post_ref = $tl{$code}; + if (!$post_ref) { + print $stdout "-- sorry, I don't know that code.\n"; + return 0; + } + $target_user = lc($post_ref->{'user'}->{'acct'} || $post_ref->{'user'}->{'username'}); + if (!$target_user) { + print $stdout "-- sorry, couldn't get user from that post.\n"; + return 0; + } + } else { + # Username provided directly + $target_user = lc($username); + } + + if ($m eq 'mute') { + my $confirm_msg = $code ? + "-- sure you want to mute $target_user (author of $code)? (only y or Y is affirmative):" : + "-- sure you want to mute $target_user? (only y or Y is affirmative):"; + $answer = lc(&linein($confirm_msg)); + if ($answer ne 'y') { + print $stdout "-- ok, $target_user is NOT muted.\n"; + return 0; + } + } + + &muteuser($target_user, 1, + (($m eq 'mute') ? $muteurl : $mutedelurl), + (($m eq 'mute') ? 'started' : 'stopped')); + return 0; + } + # list support # /withlist (/withlis, /with, /wl) if (s#^/(withlist|withlis|withl|with|wl)\s+([^/\s]+)\s+## && @@ -6260,22 +6328,8 @@ sub refresh { } } - # add stream for replies, if requested - if ($mentions) { - # same thing - my $r = &grabjson($rurl, - ($dostream && !$nostreamreplies) ? $last_id : $fetch_id, - 0, - (($last_id) ? 250 - : $fetchwanted || $backload), { - "type" => "reply", - "payload" => "" - }, 1); - push(@streams, $r) - if (defined($r) && - ref($r) eq 'ARRAY' && - scalar(@{ $r })); - } + # Mentions are included in home timeline and handled via background notifications + # No separate mentions fetch needed in fediverse (unlike Twitter) # next handle hashtags and tracktags # failure here does not abort, because search may be down independently @@ -7420,6 +7474,34 @@ sub boruuser { return 0; } +# mute or unmute a user +sub muteuser { + my $uname = shift; + my $interactive = shift; + my $basef = shift; + my $verb = shift; + + # Look up account ID for the username + my $account_id = &lookup_account_id($uname); + if (!$account_id) { + &report_account_not_found($uname, $interactive); + return 1; + } + + # Substitute account ID into URL template + my $api_url = $basef; + $api_url =~ s/%I/$account_id/g; + + # For mute, include notifications=true parameter (mute notifications too) + my $post_data = ($verb eq 'started') ? "notifications=true&id=$account_id" : "id=$account_id"; + + my ($en, $em) = ¢ral_cd_dispatch($post_data, + $interactive, $api_url); + print $stdout "-- ok, you have $verb muting user $uname.\n" + if ($interactive && !$en); + return 0; +} + # enable or disable reposts for a user sub rtsonoffuser { my $uname = shift; @@ -8551,7 +8633,7 @@ sub defaultautocompletion { '/unset', '/verbose', '/short', '/follow', '/unfollow', '/doesfollow', '/search', '/tron', '/troff', '/delete', '/deletelast', '/dump', - '/track', '/trends', '/block', '/unblock', + '/track', '/trends', '/block', '/unblock', '/mute', '/unmute', '/fave', '/faves', '/unfave', '/eval'); } @rlkeys = keys(%readline_completion);