Updated extensions.

This commit is contained in:
Storm Dragon
2025-07-29 11:29:27 -04:00
parent 224b6bfa4a
commit 2b8bf031fa
17 changed files with 875 additions and 1 deletions

View File

@ -1 +1,79 @@
Placeholder
# TTYverse Extensions
This directory contains bundled extensions for TTYverse, the command-line fediverse client.
## Extension Loading
TTYverse uses a simple XDG-compliant extension system. Extensions are only loaded from:
**`~/.local/share/ttyverse/extensions/`** (XDG_DATA_HOME/ttyverse/extensions)
Extensions are loaded with the `-exts` flag:
```bash
perl ttyverse.pl -exts=soundpack
perl ttyverse.pl -exts=extension1,extension2
```
## Managing Extensions
Use the included management script to enable/disable bundled extensions:
```bash
# Show available and enabled extensions
./extensions/manage-extensions.sh list
# Enable an extension (creates symlink in XDG directory)
./extensions/manage-extensions.sh enable soundpack
# Disable an extension (removes from XDG directory)
./extensions/manage-extensions.sh disable soundpack
# Check extension status
./extensions/manage-extensions.sh status soundpack
```
## Available Extensions
### soundpack.pl
Plays notification sounds for different types of posts (timeline, replies, DMs, mentions, search results).
**Configuration options** (add to `~/.config/ttyverse/ttyverse.rc`):
```
extpref_sound_command=paplay # Sound player (paplay, play, ogg123, etc.)
extpref_soundpack=default # Sound pack name
```
**Sound files location**: `~/.local/share/ttyverse/sounds/default/`
- `default.ogg` - Regular timeline posts
- `mention.ogg` - Mentions of your username
- `dm.ogg` - Direct messages
- `me.ogg` - Your own posts
- `follow.ogg` - New followers
- `boost.ogg` - Your posts boosted
- `favourite.ogg` - Your posts favourited
- `poll.ogg` - Poll notifications
- `announcement.ogg` - Server announcements
## Installing Custom Extensions
1. Copy your extension `.pl` file to `~/.local/share/ttyverse/extensions/`
2. Load it with `-exts=yourextension`
## Extension Development
Extensions are Perl modules that hook into TTYverse's event system. Key variables:
- `$data` - XDG data directory (`~/.local/share/ttyverse`)
- `$config` - XDG config directory (`~/.config/ttyverse`)
- `$store` - Extension workspace (persistent storage)
- `$silent` - Silent mode flag
- `$stdout` - Output filehandle
Extensions can define these functions:
- `$notifier` - Called for new posts
- `$heartbeat` - Called periodically
- `$handle` - Called for user commands
- `$prepost` - Called before posting
- `$postpost` - Called after posting
Set `$store->{'loaded'} = 1;` at the end of your extension.

70
libnotifyperl.pl Normal file
View File

@ -0,0 +1,70 @@
# TTYverse Desktop Notifications Extension (libnotify-perl)
# Adapted from original TTYtter extension
# Provides desktop notifications using GTK Gtk2::Notify
# Published under the Floodgap Free Software License: http://www.floodgap.com/software/ffsl/
# What is it?
# This extension enables desktop notifications for TTYverse using the
# libnotify system on Linux/Unix with GUI environments.
# Requirements:
# - Gtk2::Notify Perl module
# - GUI environment with DISPLAY set
# - libnotify system installed
# Installation:
# Install dependency: cpan Gtk2::Notify
# Or on Debian/Ubuntu: sudo apt install libgtk2-notify-perl
# Or on Fedora/RHEL: sudo dnf install perl-Gtk2-Notify
# Or on Arch: sudo pacman -S perl-gtk2-notify
eval { require Gtk2::Notify };
if($@){
print "-- TTYverse notification extension requires Gtk2::Notify\n";
print "-- Install with: cpan Gtk2::Notify\n";
print "-- Or on Debian/Ubuntu: sudo apt install libgtk2-notify-perl\n";
print "-- Or on Fedora/RHEL: sudo dnf install perl-Gtk2-Notify\n";
print "-- Or on Arch: sudo pacman -S perl-gtk2-notify\n";
die();
}
sub notifier_libnotifyperl {
# Skip notifications if no GUI display available
return 1 if(!$ENV{'DISPLAY'});
my $class = shift;
my $text = shift;
my $ref = shift; # Post reference data (unused in this version)
# Handle initialization
if (!defined($class) || !defined($notify_send_path)) {
if (!defined($class)) {
return 1 if ($script); # Skip in script mode
$class = 'TTYverse Desktop Notifications';
$text = 'Desktop notifications are now active for TTYverse.';
Gtk2::Notify->init('ttyverse');
print $streamout "-- $text\n" if (!$silent);
}
}
# Show notification if system is properly initialized
if (Gtk2::Notify->is_initted()) {
my $notification = Gtk2::Notify->new(
"TTYverse: $class",
$text
);
# Set reasonable timeout (5 seconds)
$notification->set_timeout(5_000);
if($notification->show()) {
# Debug output (optional)
# print $streamout "-- Notification shown: $class\n" if ($debug);
}
}
return 1;
}
# Make sure the module loads properly
1;

287
manage-extensions.sh Executable file
View File

@ -0,0 +1,287 @@
#!/usr/bin/env bash
#
# TTYverse Extension Manager
# Simple XDG-compliant extension management for ~/.local/share/ttyverse/extensions/
#
# XDG data directory for TTYverse
DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/ttyverse"
EXT_DIR="$DATA_DIR/extensions"
BUNDLED_DIR="$(dirname "$0")"
# Colors for output (only if stdout is a terminal)
if [[ -t 1 ]]; then
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
else
GREEN=''
YELLOW=''
RED=''
NC=''
fi
# Ensure extension directory exists
mkdir -p "$EXT_DIR"
show_help() {
cat << EOF
TTYverse Extension Manager
Usage: $0 <command> [extension_name]
Commands:
list Show available and enabled extensions
enable <ext> Enable an extension (symlink to XDG extensions directory)
disable <ext> Disable an extension (remove from XDG extensions directory)
status <ext> Show status of specific extension
help Show this help
Examples:
$0 list # Show all extensions
$0 enable soundpack # Enable soundpack extension
$0 disable soundpack # Disable soundpack extension
$0 status soundpack # Check if soundpack is enabled
XDG Extensions Directory: $EXT_DIR
Bundled Extensions: $BUNDLED_DIR
Note: TTYverse only loads extensions from the XDG directory.
Use this script to enable/disable bundled extensions or install your own.
EOF
}
list_extensions() {
echo -e "${GREEN}Available Extensions:${NC}"
for ext in "$BUNDLED_DIR"/*.pl; do
if [[ -f "$ext" ]]; then
basename="${ext##*/}"
name="${basename%.pl}"
if [[ -L "$EXT_DIR/$basename" || -f "$EXT_DIR/$basename" ]]; then
echo -e " ${GREEN}${NC} $name (enabled)"
else
echo -e " ${YELLOW}${NC} $name (available)"
fi
fi
done
echo -e "\n${GREEN}User Extensions:${NC}"
for ext in "$EXT_DIR"/*.pl; do
if [[ -f "$ext" && ! -L "$ext" ]]; then
basename="${ext##*/}"
name="${basename%.pl}"
echo -e " ${GREEN}${NC} $name (user-installed)"
fi
done
}
enable_extension() {
local name="$1"
local ext_file="$name.pl"
local bundled_path="$BUNDLED_DIR/$ext_file"
local user_path="$EXT_DIR/$ext_file"
if [[ ! -f "$bundled_path" ]]; then
echo -e "${RED}Error:${NC} Extension '$name' not found in bundled extensions"
echo "Available extensions:"
for ext in "$BUNDLED_DIR"/*.pl; do
if [[ -f "$ext" ]]; then
basename="${ext##*/}"
echo " ${basename%.pl}"
fi
done
return 1
fi
if [[ -L "$user_path" ]]; then
echo -e "${YELLOW}Warning:${NC} Extension '$name' is already enabled"
return 0
elif [[ -f "$user_path" ]]; then
echo -e "${YELLOW}Warning:${NC} User-installed extension '$name' already exists"
return 0
fi
ln -s "$(realpath "$bundled_path")" "$user_path"
echo -e "${GREEN}${NC} Enabled extension: $name"
echo " Linked: $bundled_path -> $user_path"
# Special handling for soundpack extension - copy default sounds and configure RC
if [[ "$name" == "soundpack" ]]; then
local sounds_src="$BUNDLED_DIR/sounds"
local sounds_dst="$DATA_DIR/sounds"
local config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/ttyverse"
local rc_file="$config_dir/ttyverserc"
# Copy sound files
if [[ -d "$sounds_src" ]]; then
echo " Copying bundled sound files..."
cp -r "$sounds_src"/* "$sounds_dst/" 2>/dev/null || true
echo -e "${GREEN}${NC} Copied default sound pack to $sounds_dst/"
fi
# Auto-configure RC file
mkdir -p "$config_dir"
local needs_config=false
# Check if soundpack extension is already configured
# Need to check for exts, notifytype, and notifies all being properly set
local has_exts=false
local has_notifytype=false
local has_notifies=false
if [[ -f "$rc_file" ]]; then
grep -q "^exts.*soundpack" "$rc_file" 2>/dev/null && has_exts=true
grep -q "^notifytype=.*soundpack" "$rc_file" 2>/dev/null && has_notifytype=true
grep -q "^notifies=.*mention.*boost.*favourite" "$rc_file" 2>/dev/null && has_notifies=true
fi
if [[ "$has_exts" == "false" ]] || [[ "$has_notifytype" == "false" ]] || [[ "$has_notifies" == "false" ]]; then
needs_config=true
fi
if [[ "$needs_config" == "true" ]]; then
echo " Configuring TTYverse for sound notifications..."
# Create backup if RC file exists
[[ -f "$rc_file" ]] && cp "$rc_file" "$rc_file.backup.$(date +%s)"
# Update or create exts line
if [[ -f "$rc_file" ]] && grep -q "^exts=" "$rc_file"; then
# Check if soundpack is already in exts line
if ! grep -q "^exts=.*soundpack" "$rc_file"; then
sed -i 's/^exts=\(.*\)/exts=\1,soundpack/' "$rc_file"
echo " Added soundpack to existing exts line"
fi
else
# Add new exts line
echo "exts=soundpack" >> "$rc_file"
echo " Added exts=soundpack"
fi
# Add or update notifytype
if [[ -f "$rc_file" ]] && grep -q "^notifytype=" "$rc_file"; then
sed -i 's/^notifytype=.*/notifytype=soundpack/' "$rc_file"
echo " Updated notifytype to soundpack"
else
echo "notifytype=soundpack" >> "$rc_file"
echo " Added notifytype=soundpack"
fi
# Add or update notifies parameter for sound events
if [[ -f "$rc_file" ]] && grep -q "^notifies=" "$rc_file"; then
# Update existing notifies line to include fediverse sound types
sed -i 's/^notifies=.*/notifies=default,mention,dm,me,follow,boost,favourite/' "$rc_file"
echo " Updated existing notifies configuration"
else
# Add new notifies line
echo "notifies=default,mention,dm,me,follow,boost,favourite" >> "$rc_file"
echo " Added notifies configuration"
fi
# Add extension preferences
{
echo ""
echo "# Sound notifications (added by extension manager)"
echo "extpref_sound_command=paplay"
echo "extpref_soundpack=default"
echo ""
echo "# Fediverse sound types: default, mention, dm, me, follow, boost, favourite, poll, announcement"
} >> "$rc_file"
echo -e "${GREEN}${NC} Configured TTYverse RC file for sound notifications"
echo " Restart TTYverse to enable sounds automatically"
else
echo -e "${YELLOW}Note:${NC} Soundpack already configured in RC file"
fi
fi
}
disable_extension() {
local name="$1"
local ext_file="$name.pl"
local user_path="$EXT_DIR/$ext_file"
if [[ ! -e "$user_path" ]]; then
echo -e "${YELLOW}Warning:${NC} Extension '$name' is not enabled"
return 0
fi
if [[ -L "$user_path" ]]; then
rm "$user_path"
echo -e "${GREEN}${NC} Disabled extension: $name"
# Special handling for soundpack - offer to remove from RC file
if [[ "$name" == "soundpack" ]]; then
local config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/ttyverse"
local rc_file="$config_dir/ttyverserc"
if [[ -f "$rc_file" ]] && grep -q "soundpack" "$rc_file"; then
echo -e "${YELLOW}Note:${NC} Soundpack is still configured in $rc_file"
echo " You may want to remove the soundpack lines to fully disable"
echo " Or run TTYverse without -exts=soundpack to override"
fi
fi
elif [[ -f "$user_path" ]]; then
echo -e "${YELLOW}Warning:${NC} '$name' is a user-installed extension (not a symlink)"
echo "Remove manually if needed: $user_path"
fi
}
show_status() {
local name="$1"
local ext_file="$name.pl"
local bundled_path="$BUNDLED_DIR/$ext_file"
local user_path="$EXT_DIR/$ext_file"
echo "Extension: $name"
echo "Bundled: $([[ -f "$bundled_path" ]] && echo "Available" || echo "Not found")"
if [[ -L "$user_path" ]]; then
echo -e "Status: ${GREEN}Enabled${NC} (symlinked)"
echo "Target: $(readlink "$user_path")"
elif [[ -f "$user_path" ]]; then
echo -e "Status: ${GREEN}Enabled${NC} (user-installed)"
else
echo -e "Status: ${YELLOW}Disabled${NC}"
fi
}
# Main command handling
case "${1:-help}" in
list|ls)
list_extensions
;;
enable)
if [[ -z "$2" ]]; then
echo -e "${RED}Error:${NC} Extension name required"
echo "Usage: $0 enable <extension_name>"
exit 1
fi
enable_extension "$2"
;;
disable)
if [[ -z "$2" ]]; then
echo -e "${RED}Error:${NC} Extension name required"
echo "Usage: $0 disable <extension_name>"
exit 1
fi
disable_extension "$2"
;;
status)
if [[ -z "$2" ]]; then
echo -e "${RED}Error:${NC} Extension name required"
echo "Usage: $0 status <extension_name>"
exit 1
fi
show_status "$2"
;;
help|--help|-h)
show_help
;;
*)
echo -e "${RED}Error:${NC} Unknown command: $1"
echo "Run '$0 help' for usage information"
exit 1
;;
esac

76
soundpack.pl Normal file
View File

@ -0,0 +1,76 @@
# TTYverse Sound Pack Extension
# Adapted from the original TTYtter soundpack by Storm Dragon: https://stormux.org/
# Support Storm Dragon's work: https://patreon.com/stormux
# Published under the Floodgap Free Software License: http://www.floodgap.com/software/ffsl/
# Plays sounds for different fediverse activities:
# Core: default, mention, dm, me
# Fediverse: follow, boost, favourite, poll, announcement
#
# Configure in RC file with:
# extpref_sound_command=paplay # Sound command (paplay, play, ogg123, etc.)
# extpref_soundpack=default # Sound pack name
# notifytype=soundpack # Enable sound notifications
# notifies=default,mention,dm,me,follow,boost,favourite
sub notifier_soundpack {
my $class = shift;
my $text = shift;
my $ref = shift;
# Debug output
print $stdout "-- DEBUG: soundpack notifier called with class='$class'\n" if (defined($class) && $verbose);
# Determine sound command (default to paplay for modern Linux)
my $sound_command;
if (!$extpref_sound_command) {
$sound_command = "paplay"; # Modern default for PulseAudio
} else {
$sound_command = $extpref_sound_command;
}
# Determine sound pack name
my $sound_pack;
if (!$extpref_soundpack) {
$sound_pack = "default";
} else {
$sound_pack = $extpref_soundpack;
}
# Skip if silent mode enabled
return 1 if ($silent);
if (!defined($class)) {
# Initialize - show loaded message
print $stdout "-- TTYverse sound pack loaded: $sound_pack\n";
print $stdout "-- Supported sounds: default, mention, dm, me, follow, boost, favourite, poll, announcement\n" if (!$silent);
return 1;
}
# Build sound file path using XDG data directory
my $data_dir = "$ENV{'HOME'}/.local/share/ttyverse";
my $sound_file = "$data_dir/sounds/$sound_pack/" . lc($class) . ".ogg";
if (!-f $sound_file) {
# Only warn once per session per class
my $warn_key = "warned_missing_sound_$class";
if (!$store->{$warn_key}) {
print $stdout "-- Warning: Sound file '$class.ogg' not found in pack '$sound_pack'\n";
print $stdout "-- Place sound files in $data_dir/sounds/$sound_pack/\n";
$store->{$warn_key} = 1;
}
return 1;
}
# Play the sound in background (suppress output)
print $stdout "-- DEBUG: Playing sound: $sound_command \"$sound_file\"\n" if ($verbose);
system("$sound_command \"$sound_file\" >/dev/null 2>&1 &");
return 1;
}
# Mark extension as successfully loaded
$store->{'loaded'} = 1;
# Extension loaded successfully
1;

4
sounds/.gitattributes vendored Normal file
View File

@ -0,0 +1,4 @@
*.wav filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.opus filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text

119
sounds/README.md Normal file
View File

@ -0,0 +1,119 @@
# TTYverse Sound Packs
TTYverse supports audio notifications for different types of fediverse activity. Sound packs are collections of audio files that play when specific events occur.
## Quick Start
1. **Enable sounds**: Use the extension manager to enable the soundpack extension:
```bash
./extensions/manage-extensions.sh enable soundpack
```
2. **Configure TTYverse**: Add to your `~/.config/ttyverse/ttyverserc`:
```
notifytype=soundpack
notifies=default,mention,dm,me,search,follow,boost,favourite
extpref_soundpack=default
extpref_sound_command=paplay
```
3. **Test**: Start TTYverse and you should hear sounds for different activities!
## Sound Pack Structure
Sound packs are stored in `~/.local/share/ttyverse/sounds/` with this structure:
```
sounds/
├── default/ # Default sound pack (included)
│ ├── default.ogg # Regular posts
│ ├── mention.ogg # @mentions of you
│ ├── dm.ogg # Direct messages
│ ├── me.ogg # Your own posts
│ ├── follow.ogg # New followers
│ ├── boost.ogg # Your posts boosted
│ ├── favourite.ogg # Your posts favourited
│ ├── poll.ogg # Poll notifications
│ └── announcement.ogg # Server announcements
├── custom/ # Your custom sound pack
│ └── *.ogg
└── README.md # This file
```
## Required Sound Files
### Core Sounds (Essential)
- **`default.ogg`** - Regular timeline posts
- **`mention.ogg`** - When someone @mentions you
- **`dm.ogg`** - Direct messages received
- **`me.ogg`** - Your own posts (optional feedback)
### Fediverse Interaction Sounds
- **`follow.ogg`** - Someone follows you
- **`boost.ogg`** - Someone boosts (shares) your post
- **`favourite.ogg`** - Someone favourites (likes) your post
- **`poll.ogg`** - Poll results or poll you voted in ends
- **`announcement.ogg`** - Server announcements
## Creating Custom Sound Packs
1. **Create directory**: Make a new directory under `sounds/` with your pack name:
```bash
mkdir -p ~/.local/share/ttyverse/sounds/mystyle
```
2. **Add sound files**: Place `.ogg` files with the required names. All files should be:
- **Format**: OGG Vorbis (most compatible)
- **Length**: 1-3 seconds recommended (short and sweet)
- **Volume**: Normalized to prevent startling users
- **Sample Rate**: 44.1kHz or 48kHz
3. **Configure TTYverse**: Update your config to use the new pack:
```
extpref_soundpack=mystyle
```
4. **Test**: Restart TTYverse to load the new sounds
## Audio Format Support
TTYverse uses your system's audio command for playback. Supported formats depend on your audio player:
- **paplay** (PulseAudio default): OGG, WAV, FLAC
- **play** (SoX): Most formats including MP3, OGG, WAV
- **ogg123**: OGG Vorbis only
- **mpv**: Most formats including MP3, OGG, WAV, FLAC
Configure your preferred player with:
```
extpref_sound_command=paplay # or play, ogg123, mpv, etc.
```
## Troubleshooting
### No Sound
1. Check audio command: `paplay /usr/share/sounds/alsa/Front_Left.wav`
2. Verify sound pack path: `ls ~/.local/share/ttyverse/sounds/`
3. Check TTYverse config: `grep -E "(notifytype|soundpack)" ~/.config/ttyverse/ttyverserc`
4. Test with verbose mode: TTYverse will show warnings for missing sounds
### Wrong Sounds Playing
- Verify file names exactly match the required names (case-sensitive)
- Check that you're using the correct sound pack name in config
- Restart TTYverse after changing sound files
### Performance Issues
- Use compressed formats like OGG instead of WAV
- Keep sound files under 3 seconds
- Use `&` in sound command for background playback (already handled by extension)
## Example Sound Pack Themes
### **Retro Gaming**
Use classic 8-bit style beeps and boops for different actions
### **Natural Sounds**
Bird calls, water drops, wind chimes for a calming experience
### **Minimal**
Simple tones with different pitches for each notification type

BIN
sounds/default/announcement.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/default/boost.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/default/default.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/default/dm.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/default/favourite.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/default/follow.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/default/me.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/default/mention.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/default/poll.ogg (Stored with Git LFS) Normal file

Binary file not shown.

60
timestamp.pl Normal file
View File

@ -0,0 +1,60 @@
# TTYverse Timestamp Extension
# Modified by Storm Dragon: https://stormux.org/
# Support Storm Dragon's work: https://patreon.com/stormux
# Original concept from kosertech.com (see ttyverse.wiki/Extension-Development.md for development guide)
# Published under the Floodgap Free Software License: http://www.floodgap.com/software/ffsl/
# What is it?
# This extension adds timestamps before posts when 5 minutes or more have passed
# between timeline updates, helping organize the flow of your fediverse timeline.
# Usage:
# This extension works automatically. Load it when you start TTYverse or add it
# to your configuration. The timestamp format is: -- HH:MMAM/PM --
$handle = sub {
my $delayInSeconds = 60 * 5; # 5 minutes default
my ($nsec,$nmin,$nhour,$nday,$nmon,$nyear) = localtime(time);
# Initialize timestamp tracking on first run
if(!$Lib_firstrun){
$Lib_firstrun = 1;
print "-- TTYverse timestamp extension loaded\n" if (!$silent);
$Lib_past = time;
}
# Print timestamp if enough time has elapsed
if($Lib_past < (time - $delayInSeconds)){
$Lib_past = time;
my $timeString = "-- ";
my $timeOfDay = "AM";
# Convert to 12-hour format
if ($nhour >= 12) {
if ($nhour > 12) {
$nhour = $nhour - 12;
}
$timeOfDay = "PM";
}
if (($nhour < 10) && ($nhour != 0)) {
$timeString .= "0";
}
if ($nhour == 0) {
$nhour = 12;
$timeOfDay = "AM";
}
$timeString .= "$nhour:";
if ($nmin < 10) {
$timeString .= "0";
}
$timeString .= "$nmin$timeOfDay";
print $streamout "$timeString --\n" if (!$silent);
}
# Allow TTYverse to handle displaying the post normally
my $ref = shift;
&defaulthandle($ref);
return 1;
};

153
tts.pl Normal file
View File

@ -0,0 +1,153 @@
# TTYverse Text-to-Speech Extension
# By Storm Dragon: https://stormux.org/
# Support Storm Dragon's work: https://patreon.com/stormux
# Published under the Floodgap Free Software License: http://www.floodgap.com/software/ffsl/
# What is it?
# This extension uses text-to-speech to read incoming fediverse posts.
# Multiple synthesizer engines are supported for accessibility.
# Usage:
# Toggle TTS on/off: /tts
# Get help: /tts help
# Load extension when starting TTYverse or add to configuration
$addaction = sub {
my $command = shift;
my $speak = "";
# Get current TTS state from config
my $tts_config = "$ENV{'HOME'}/.config/ttyverse/tts_enabled";
if (-e $tts_config) {
open TTSSETTINGS, $tts_config;
$speak = <TTSSETTINGS>;
close TTSSETTINGS;
chomp($speak) if defined($speak);
} else {
$speak = 0;
}
if ($command eq '/tts') {
if ((!defined($speak)) || ($speak eq 1)) {
$speak = 0;
print $streamout "-- Text-to-speech disabled\n";
} else {
$speak = 1;
print $streamout "-- Text-to-speech enabled\n";
}
# Save new state
open TTSSETTINGS, ">$tts_config";
print TTSSETTINGS "$speak";
close TTSSETTINGS;
return 1;
}
if ($command eq "/tts help") {
my $ttsHelp = "TTYverse Text-to-Speech Extension\n\n" .
"Commands:\n" .
" /tts - Toggle speech on/off\n" .
" /tts help - Show this help\n\n" .
"Configuration (add to TTYverse config):\n\n" .
"extpref_tts_synthesizer=synthName\n" .
" Available: espeak (default), festival, pico, cepstral, mac\n\n" .
"extpref_tts_language=languageCode\n" .
" Default: en-US (supported by espeak and pico)\n\n" .
"extpref_tts_rate=number\n" .
" Speech rate, default: 175 (espeak only)\n\n" .
"extpref_tts_variant=name\n" .
" Voice variant/name (espeak variants, cepstral/mac voices)\n";
print $streamout "$ttsHelp";
return 1;
}
return 0;
};
$handle = sub {
my $ref = shift;
my $class = shift;
# Skip TTS during initial timeline load
if ($last_id eq 0) {
&defaulthandle($ref);
return 1;
}
my $speak = "";
# Get TTS state from config
my $tts_config = "$ENV{'HOME'}/.config/ttyverse/tts_enabled";
if (-e $tts_config) {
open TTSSETTINGS, $tts_config;
$speak = <TTSSETTINGS>;
close TTSSETTINGS;
chomp($speak) if defined($speak);
} else {
$speak = 0;
}
# Initialize TTS configuration
my $soundCommand = "paplay"; # Default for modern Linux/PulseAudio
my $synthesizer = $extpref_tts_synthesizer || "espeak";
my $language = $extpref_tts_language || "en-US";
my $variant = $extpref_tts_variant || "";
my $rate = $extpref_tts_rate || "";
# Override sound command if configured
if ($extpref_sound_command) {
$soundCommand = $extpref_sound_command;
}
# Extract post content for TTS
my $postText = &descape($ref->{'account'}->{'display_name'} || $ref->{'account'}->{'username'}) .
" posted: " . &descape($ref->{'content'});
# Make speech more accessible
$postText =~ s/\"/ /g; # Remove quotes
$postText =~ s/ \#/ hashtag /g; # Make hashtags readable
$postText =~ s/https?:\/\/\S+/ link /g; # Simplify links
$postText =~ s/www\./ /g; # Remove www
$postText =~ s/\// forward slash /g; # Make slashes readable
$postText =~ s/\\/ back slash /g; # Make backslashes readable
$postText =~ s/\blol\b/ laughs out loud /gi; # Expand common abbreviations
$postText =~ s/\brofl\b/ rolling on the floor laughing /gi;
$postText =~ s/<[^>]*>//g; # Remove any remaining HTML
my $speechCommand = "";
# Configure synthesizer command
if ($synthesizer eq "cepstral") {
$speechCommand = "aoss swift ";
if ($variant ne "") {
$speechCommand .= "-n $variant ";
}
$speechCommand .= "\"$postText\"";
} elsif ($synthesizer eq "espeak") {
if ($rate eq "") {
$rate = "175";
}
$speechCommand = "$synthesizer -v " . lc($language);
if ($variant ne "") {
$speechCommand .= "+$variant";
}
$speechCommand .= " -s $rate \"$postText\"";
} elsif ($synthesizer eq "festival") {
$speechCommand = "echo \"$postText\" | festival --tts";
} elsif ($synthesizer eq "mac") {
$speechCommand = "say ";
if ($variant ne "") {
$speechCommand .= "-v $variant ";
}
$speechCommand .= "\"$postText\"";
} elsif ($synthesizer eq "pico") {
$speechCommand = "pico2wave -l $language -w /tmp/ttyverse.wav \"$postText\" && $soundCommand /tmp/ttyverse.wav";
}
# Speak the post if TTS is enabled
if ($speak eq 1) {
system("$speechCommand");
}
# Allow TTYverse to display the post normally
&defaulthandle($ref);
return 1;
};