Updated extensions information.
							
								
								
									
										576
									
								
								Extension-Development.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
										
											
											
										
										
									
								
							
						
						
									
										576
									
								
								Extension-Development.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,576 @@
 | 
			
		||||
# Extension Development
 | 
			
		||||
 | 
			
		||||
TTYverse extensions are Perl scripts that integrate with TTYverse's event system to add custom functionality. This guide covers creating, testing, and deploying custom extensions.
 | 
			
		||||
 | 
			
		||||
## Extension Architecture
 | 
			
		||||
 | 
			
		||||
Extensions hook into TTYverse's multi-process architecture through defined callback functions:
 | 
			
		||||
 | 
			
		||||
- **`$handle`** - Called for each incoming post (main processing hook)
 | 
			
		||||
- **`$addaction`** - Called for user commands (adds new slash commands)
 | 
			
		||||
- **`$prepost`** - Called before sending posts
 | 
			
		||||
- **`$postpost`** - Called after sending posts  
 | 
			
		||||
- **`$heartbeat`** - Called periodically for background tasks
 | 
			
		||||
- **`$notifier`** - Called for notification events (sounds, desktop alerts, etc.)
 | 
			
		||||
 | 
			
		||||
## Available Variables
 | 
			
		||||
 | 
			
		||||
Extensions have access to these TTYverse variables:
 | 
			
		||||
 | 
			
		||||
### Core Variables
 | 
			
		||||
- **`$whoami`** - Current user's username
 | 
			
		||||
- **`$silent`** - Silent mode flag (suppress output when true)
 | 
			
		||||
- **`$verbose`** - Verbose mode flag (show debug info when true)
 | 
			
		||||
- **`$stdout`** - Output filehandle for user messages
 | 
			
		||||
- **`$streamout`** - Output filehandle for timeline content
 | 
			
		||||
 | 
			
		||||
### File Paths  
 | 
			
		||||
- **`$data`** - XDG data directory (`~/.local/share/ttyverse`)
 | 
			
		||||
- **`$config`** - XDG config directory (`~/.config/ttyverse`) 
 | 
			
		||||
- **`$store`** - Extension persistent storage hash
 | 
			
		||||
 | 
			
		||||
### Post Processing
 | 
			
		||||
- **`&defaulthandle($ref)`** - Default post display function
 | 
			
		||||
- **`&standardpost($ref, $text_only)`** - Format post for notifications
 | 
			
		||||
- **`&findtarget($code)`** - Find post by menu code (a1, b3, etc.)
 | 
			
		||||
- **`&descape($text)`** - Decode HTML entities and escape sequences safely
 | 
			
		||||
- **`&common_split_post($text, $reply_to, $media)`** - Send posts via TTYverse
 | 
			
		||||
 | 
			
		||||
### Timeline State
 | 
			
		||||
- **`$last_id`** - Current timeline position (0 during initial load)
 | 
			
		||||
- **`$initial_load_in_progress`** - True during startup timeline fetch
 | 
			
		||||
 | 
			
		||||
### Environment Access
 | 
			
		||||
- **`$ENV{'HOME'}`** - User's home directory
 | 
			
		||||
- **`$ENV{'DISPLAY'}`** - X11 display (for GUI detection)
 | 
			
		||||
- **`$ENV{'XDG_DATA_HOME'}`** - XDG data directory override
 | 
			
		||||
- **`$ENV{'XDG_CONFIG_HOME'}`** - XDG config directory override
 | 
			
		||||
 | 
			
		||||
## Extension Types
 | 
			
		||||
 | 
			
		||||
### 1. Post Processing Extensions
 | 
			
		||||
 | 
			
		||||
Handle incoming posts and modify display:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
$handle = sub {
 | 
			
		||||
    my $post_ref = shift;
 | 
			
		||||
    my $class = shift;  # Notification class (default, mention, dm, etc.)
 | 
			
		||||
    
 | 
			
		||||
    # Your custom processing here
 | 
			
		||||
    # Access post data: $post_ref->{'content'}, $post_ref->{'account'}->{'username'}
 | 
			
		||||
    
 | 
			
		||||
    # Always call default handler to display the post
 | 
			
		||||
    &defaulthandle($post_ref);
 | 
			
		||||
    return 1;
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 2. Command Extensions
 | 
			
		||||
 | 
			
		||||
Add new slash commands:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
$addaction = sub {
 | 
			
		||||
    my $command_line = $_;  # Full command line input
 | 
			
		||||
    
 | 
			
		||||
    if ($command_line =~ /^\/mycommand\s+(.*)$/) {
 | 
			
		||||
        my $args = $1;
 | 
			
		||||
        
 | 
			
		||||
        # Process your command
 | 
			
		||||
        print $streamout "-- My command executed with: $args\n";
 | 
			
		||||
        return 1;  # Command handled
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return 0;  # Not our command, let TTYverse handle it
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3. Notification Extensions
 | 
			
		||||
 | 
			
		||||
Handle sound, visual, or other notifications:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
sub notifier_mynotifier {
 | 
			
		||||
    my $class = shift;    # Notification type (dm, mention, boost, etc.)
 | 
			
		||||
    my $text = shift;     # Formatted notification text  
 | 
			
		||||
    my $post_ref = shift; # Original post data
 | 
			
		||||
    
 | 
			
		||||
    # Handle initialization (called with no $class)
 | 
			
		||||
    if (!defined($class)) {
 | 
			
		||||
        print $stdout "-- My notifier loaded\n";
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Process notification based on class
 | 
			
		||||
    if ($class eq 'dm') {
 | 
			
		||||
        # Handle direct messages with higher priority
 | 
			
		||||
    } elsif ($class eq 'mention') {
 | 
			
		||||
        # Handle mentions
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Register the notifier (must be done globally)
 | 
			
		||||
# TTYverse automatically finds functions named notifier_*
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 4. Time-based Extensions
 | 
			
		||||
 | 
			
		||||
Extensions that perform actions based on time intervals:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
# Extension global variables for state tracking
 | 
			
		||||
my $last_action_time = 0;
 | 
			
		||||
my $first_run = 0;
 | 
			
		||||
 | 
			
		||||
$handle = sub {
 | 
			
		||||
    my $post_ref = shift;
 | 
			
		||||
    
 | 
			
		||||
    # Initialize on first run
 | 
			
		||||
    if (!$first_run) {
 | 
			
		||||
        $first_run = 1;
 | 
			
		||||
        $last_action_time = time;
 | 
			
		||||
        print $stdout "-- Time-based extension loaded\n" if (!$silent);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Check if enough time has passed (5 minutes = 300 seconds)
 | 
			
		||||
    my $current_time = time;
 | 
			
		||||
    if ($current_time - $last_action_time >= 300) {
 | 
			
		||||
        $last_action_time = $current_time;
 | 
			
		||||
        
 | 
			
		||||
        # Perform periodic action
 | 
			
		||||
        my ($sec,$min,$hour,$day,$mon,$year) = localtime($current_time);
 | 
			
		||||
        my $timestamp = sprintf("%02d:%02d", $hour, $min);
 | 
			
		||||
        print $streamout "-- $timestamp --\n" if (!$silent);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Always call default handler
 | 
			
		||||
    &defaulthandle($post_ref);
 | 
			
		||||
    return 1;
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Configuration and State Management
 | 
			
		||||
 | 
			
		||||
### RC File Configuration
 | 
			
		||||
 | 
			
		||||
Extensions can use configuration variables from the user's RC file:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
# In user's ~/.config/ttyverse/ttyverserc:
 | 
			
		||||
# extpref_myext_setting=value
 | 
			
		||||
# extpref_myext_enabled=1
 | 
			
		||||
 | 
			
		||||
# In your extension:
 | 
			
		||||
my $setting = $extpref_myext_setting || "default_value";
 | 
			
		||||
my $enabled = $extpref_myext_enabled || 0;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### File-based Configuration Storage
 | 
			
		||||
 | 
			
		||||
For settings that change during runtime, use separate config files:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
# Read extension-specific config file
 | 
			
		||||
my $config_file = "$ENV{'HOME'}/.config/ttyverse/myext_settings";
 | 
			
		||||
my $setting_value;
 | 
			
		||||
 | 
			
		||||
if (-e $config_file) {
 | 
			
		||||
    open my $fh, '<', $config_file or die "Cannot read config: $!";
 | 
			
		||||
    $setting_value = <$fh>;
 | 
			
		||||
    close $fh;
 | 
			
		||||
    chomp($setting_value) if defined($setting_value);
 | 
			
		||||
} else {
 | 
			
		||||
    $setting_value = "default";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Write extension-specific config file
 | 
			
		||||
sub save_setting {
 | 
			
		||||
    my $value = shift;
 | 
			
		||||
    my $config_file = "$ENV{'HOME'}/.config/ttyverse/myext_settings";
 | 
			
		||||
    
 | 
			
		||||
    open my $fh, '>', $config_file or die "Cannot write config: $!";
 | 
			
		||||
    print $fh $value;
 | 
			
		||||
    close $fh;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### XDG Directory Handling
 | 
			
		||||
 | 
			
		||||
Use XDG standards for cross-platform compatibility:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
# Get XDG-compliant data directory
 | 
			
		||||
my $data_dir = $ENV{'XDG_DATA_HOME'} || "$ENV{'HOME'}/.local/share";
 | 
			
		||||
my $ext_data_dir = "$data_dir/ttyverse";
 | 
			
		||||
 | 
			
		||||
# Get XDG-compliant config directory  
 | 
			
		||||
my $config_dir = $ENV{'XDG_CONFIG_HOME'} || "$ENV{'HOME'}/.config";
 | 
			
		||||
my $ext_config_dir = "$config_dir/ttyverse";
 | 
			
		||||
 | 
			
		||||
# Create directories if needed
 | 
			
		||||
mkdir $ext_data_dir unless -d $ext_data_dir;
 | 
			
		||||
mkdir $ext_config_dir unless -d $ext_config_dir;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Persistent Storage with $store
 | 
			
		||||
 | 
			
		||||
Use the `$store` hash for data that persists during the TTYverse session:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
# Store data (survives between function calls)
 | 
			
		||||
$store->{'my_counter'} = 0;
 | 
			
		||||
$store->{'user_preferences'} = { volume => 50, enabled => 1 };
 | 
			
		||||
 | 
			
		||||
# Access stored data
 | 
			
		||||
my $count = $store->{'my_counter'} || 0;
 | 
			
		||||
$count++;
 | 
			
		||||
$store->{'my_counter'} = $count;
 | 
			
		||||
 | 
			
		||||
# Complex data structures
 | 
			
		||||
my $prefs = $store->{'user_preferences'} || {};
 | 
			
		||||
$prefs->{'last_used'} = time;
 | 
			
		||||
$store->{'user_preferences'} = $prefs;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Post Data Structure
 | 
			
		||||
 | 
			
		||||
Post references contain Mastodon API data:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
$post_ref = {
 | 
			
		||||
    'id' => '12345',
 | 
			
		||||
    'content' => 'Post text with <em>HTML</em>',
 | 
			
		||||
    'account' => {
 | 
			
		||||
        'username' => 'user',
 | 
			
		||||
        'acct' => 'user@domain.com',
 | 
			
		||||
        'display_name' => 'Display Name'
 | 
			
		||||
    },
 | 
			
		||||
    'mentions' => [
 | 
			
		||||
        {
 | 
			
		||||
            'username' => 'mentioned_user',
 | 
			
		||||
            'acct' => 'mentioned_user@domain.com'
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    'created_at' => '2024-01-01T12:00:00Z',
 | 
			
		||||
    'visibility' => 'public'
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Notification Classes
 | 
			
		||||
 | 
			
		||||
TTYverse uses these notification classes:
 | 
			
		||||
 | 
			
		||||
- **`default`** - Regular timeline posts
 | 
			
		||||
- **`mention`** - Posts that mention your username
 | 
			
		||||
- **`dm`** - Direct messages (highest priority)
 | 
			
		||||
- **`me`** - Your own posts
 | 
			
		||||
- **`follow`** - New followers
 | 
			
		||||
- **`boost`** - Your posts were boosted/shared
 | 
			
		||||
- **`favourite`** - Your posts were favourited/liked
 | 
			
		||||
- **`poll`** - Poll notifications
 | 
			
		||||
- **`announcement`** - Server announcements
 | 
			
		||||
 | 
			
		||||
## Example: Complete Extension
 | 
			
		||||
 | 
			
		||||
Here's a comprehensive example extension that demonstrates multiple patterns:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
# Advanced Custom Extension
 | 
			
		||||
# Adds /greet command and tracks greeting statistics
 | 
			
		||||
# Demonstrates configuration, state management, and safe text processing
 | 
			
		||||
 | 
			
		||||
# Extension configuration with defaults
 | 
			
		||||
my $enabled = $extpref_greet_enabled || 1;
 | 
			
		||||
my $greeting_style = $extpref_greet_style || "friendly";
 | 
			
		||||
 | 
			
		||||
# Initialize extension state
 | 
			
		||||
if (!$store->{'greet_loaded'}) {
 | 
			
		||||
    $store->{'greet_loaded'} = 1;
 | 
			
		||||
    $store->{'greet_count'} = 0;
 | 
			
		||||
    print $stdout "-- Greeting extension loaded (style: $greeting_style)\n" if (!$silent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Command handler
 | 
			
		||||
$addaction = sub {
 | 
			
		||||
    my $command = $_;
 | 
			
		||||
    
 | 
			
		||||
    # Toggle extension on/off
 | 
			
		||||
    if ($command eq '/greet toggle') {
 | 
			
		||||
        my $config_file = "$ENV{'HOME'}/.config/ttyverse/greet_enabled";
 | 
			
		||||
        my $new_state = $enabled ? 0 : 1;
 | 
			
		||||
        
 | 
			
		||||
        open my $fh, '>', $config_file or die "Cannot save config: $!";
 | 
			
		||||
        print $fh $new_state;
 | 
			
		||||
        close $fh;
 | 
			
		||||
        
 | 
			
		||||
        $enabled = $new_state;
 | 
			
		||||
        my $status = $enabled ? "enabled" : "disabled";
 | 
			
		||||
        print $streamout "-- Greeting extension $status\n";
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Show greeting statistics
 | 
			
		||||
    if ($command eq '/greet stats') {
 | 
			
		||||
        my $count = $store->{'greet_count'} || 0;
 | 
			
		||||
        print $streamout "-- Total greetings sent: $count\n";
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Send greeting
 | 
			
		||||
    if ($command =~ /^\/greet\s+(\S+)\s*(.*)$/) {
 | 
			
		||||
        return 0 unless $enabled;  # Respect user preference
 | 
			
		||||
        
 | 
			
		||||
        my $username = $1;
 | 
			
		||||
        my $custom_message = $2;
 | 
			
		||||
        
 | 
			
		||||
        # Clean and validate username
 | 
			
		||||
        $username = &descape($username);
 | 
			
		||||
        $username =~ s/^@//;  # Remove @ if user included it
 | 
			
		||||
        
 | 
			
		||||
        # Choose greeting based on style and custom message
 | 
			
		||||
        my $greeting;
 | 
			
		||||
        if ($custom_message) {
 | 
			
		||||
            $greeting = &descape($custom_message);
 | 
			
		||||
        } elsif ($greeting_style eq "formal") {
 | 
			
		||||
            $greeting = "Good day!";
 | 
			
		||||
        } elsif ($greeting_style eq "casual") {
 | 
			
		||||
            $greeting = "Hey there! 😊";
 | 
			
		||||
        } else {  # friendly (default)
 | 
			
		||||
            $greeting = "Hello! 👋";
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        # Send the greeting
 | 
			
		||||
        my $post_text = "\@$username $greeting";
 | 
			
		||||
        my $result = &common_split_post($post_text, undef, undef);
 | 
			
		||||
        
 | 
			
		||||
        # Update statistics on success
 | 
			
		||||
        if ($result) {
 | 
			
		||||
            $store->{'greet_count'}++;
 | 
			
		||||
            print $stdout "-- Greeting sent to $username (total: $store->{'greet_count'})\n" if ($verbose);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return $result;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return 0;  # Not our command
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
# Post handler (optional - could add auto-greeting detection)
 | 
			
		||||
$handle = sub {
 | 
			
		||||
    my $post_ref = shift;
 | 
			
		||||
    my $class = shift;
 | 
			
		||||
    
 | 
			
		||||
    # Skip during initial load
 | 
			
		||||
    if ($last_id eq 0) {
 | 
			
		||||
        &defaulthandle($post_ref);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Could add logic here to detect greetings and respond
 | 
			
		||||
    # For now, just process normally
 | 
			
		||||
    &defaulthandle($post_ref);
 | 
			
		||||
    return 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
# Extension successfully loaded
 | 
			
		||||
$store->{'loaded'} = 1;
 | 
			
		||||
 | 
			
		||||
# Required return value
 | 
			
		||||
1;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Extension Development Workflow
 | 
			
		||||
 | 
			
		||||
### 1. Create Extension File
 | 
			
		||||
```bash
 | 
			
		||||
# Create extension in the extensions directory
 | 
			
		||||
~/.local/share/ttyverse/extensions/myextension.pl
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 2. Test Extension
 | 
			
		||||
```bash
 | 
			
		||||
# Test Perl syntax
 | 
			
		||||
perl -c ~/.local/share/ttyverse/extensions/myextension.pl
 | 
			
		||||
 | 
			
		||||
# Load extension with TTYverse
 | 
			
		||||
./ttyverse.pl -exts=myextension -verbose
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3. Debug Extension
 | 
			
		||||
Use verbose mode to see debug output:
 | 
			
		||||
```bash
 | 
			
		||||
./ttyverse.pl -exts=myextension -verbose
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Add debug output in your extension:
 | 
			
		||||
```perl
 | 
			
		||||
print $stdout "-- DEBUG: My extension called\n" if ($verbose);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Advanced Extension Patterns
 | 
			
		||||
 | 
			
		||||
### Handling Initial Timeline Load
 | 
			
		||||
 | 
			
		||||
Prevent extension spam during startup by checking timeline state:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
$handle = sub {
 | 
			
		||||
    my $post_ref = shift;
 | 
			
		||||
    my $class = shift;
 | 
			
		||||
    
 | 
			
		||||
    # Skip processing during initial timeline load
 | 
			
		||||
    if ($last_id eq 0) {
 | 
			
		||||
        &defaulthandle($post_ref);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Or use the more explicit flag
 | 
			
		||||
    if ($initial_load_in_progress) {
 | 
			
		||||
        &defaulthandle($post_ref);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Your extension logic here...
 | 
			
		||||
    &defaulthandle($post_ref);
 | 
			
		||||
    return 1;
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Safe Text Processing  
 | 
			
		||||
 | 
			
		||||
Always use `&descape()` when processing post content:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
# Extract and clean post text
 | 
			
		||||
my $author = &descape($post_ref->{'account'}->{'display_name'} || 
 | 
			
		||||
                     $post_ref->{'account'}->{'username'});
 | 
			
		||||
my $content = &descape($post_ref->{'content'});
 | 
			
		||||
 | 
			
		||||
# Additional cleaning for text-to-speech or notifications
 | 
			
		||||
$content =~ s/<[^>]*>//g;           # Remove HTML tags
 | 
			
		||||
$content =~ s/&[a-zA-Z0-9]+;//g;    # Remove remaining entities  
 | 
			
		||||
$content =~ s/https?:\/\/\S+/ link /g; # Replace URLs
 | 
			
		||||
$content =~ s/#(\w+)/ hashtag $1 /g;   # Make hashtags readable
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Environment Detection
 | 
			
		||||
 | 
			
		||||
Check system capabilities before using features:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
# Check for GUI environment
 | 
			
		||||
if (!$ENV{'DISPLAY'}) {
 | 
			
		||||
    print $stdout "-- Warning: No GUI display available\n" if (!$silent);
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check for required commands
 | 
			
		||||
sub command_exists {
 | 
			
		||||
    my $cmd = shift;
 | 
			
		||||
    return system("which $cmd >/dev/null 2>&1") == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (!command_exists('paplay')) {
 | 
			
		||||
    print $stdout "-- Warning: paplay not found, trying alternative\n" if (!$silent);
 | 
			
		||||
    # Try alternative commands...
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Notification Function Registration
 | 
			
		||||
 | 
			
		||||
Proper naming convention for notification handlers:
 | 
			
		||||
 | 
			
		||||
```perl
 | 
			
		||||
# Function name must be: notifier_extensionname
 | 
			
		||||
sub notifier_soundpack {
 | 
			
		||||
    my $class = shift;
 | 
			
		||||
    my $text = shift; 
 | 
			
		||||
    my $post_ref = shift;
 | 
			
		||||
    
 | 
			
		||||
    # TTYverse automatically discovers and calls functions matching this pattern
 | 
			
		||||
    # No explicit registration needed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Multiple notifiers can exist - TTYverse calls all of them
 | 
			
		||||
sub notifier_myother {
 | 
			
		||||
    # Another notification handler
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Best Practices
 | 
			
		||||
 | 
			
		||||
### Error Handling
 | 
			
		||||
```perl
 | 
			
		||||
$handle = sub {
 | 
			
		||||
    my $post_ref = shift;
 | 
			
		||||
    
 | 
			
		||||
    eval {
 | 
			
		||||
        # Your processing code here
 | 
			
		||||
        # ...
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    if ($@) {
 | 
			
		||||
        print $stdout "-- Extension error: $@\n" if (!$silent);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Always call default handler
 | 
			
		||||
    &defaulthandle($post_ref);
 | 
			
		||||
    return 1;
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Persistent Storage
 | 
			
		||||
```perl
 | 
			
		||||
# Store data between sessions
 | 
			
		||||
$store->{'my_data'} = "persistent value";
 | 
			
		||||
 | 
			
		||||
# Access stored data
 | 
			
		||||
my $saved_value = $store->{'my_data'} || "default";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Respecting User Preferences
 | 
			
		||||
```perl
 | 
			
		||||
# Check if user wants your extension to be quiet
 | 
			
		||||
return 1 if ($silent);
 | 
			
		||||
 | 
			
		||||
# Only show messages in verbose mode
 | 
			
		||||
print $stdout "-- Extension info\n" if ($verbose && !$silent);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Extension Loading
 | 
			
		||||
 | 
			
		||||
Extensions must end with:
 | 
			
		||||
```perl
 | 
			
		||||
# Mark extension as successfully loaded
 | 
			
		||||
$store->{'loaded'} = 1;
 | 
			
		||||
 | 
			
		||||
# Required Perl module return value
 | 
			
		||||
1;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Testing Your Extension
 | 
			
		||||
 | 
			
		||||
1. **Syntax Check**: `perl -c yourextension.pl`
 | 
			
		||||
2. **Load Test**: `./ttyverse.pl -exts=yourextension -verbose`
 | 
			
		||||
3. **Function Test**: Try your commands/hooks with real data
 | 
			
		||||
4. **Error Test**: Test with invalid inputs and network failures
 | 
			
		||||
 | 
			
		||||
## Distribution
 | 
			
		||||
 | 
			
		||||
To share your extension:
 | 
			
		||||
 | 
			
		||||
1. Add clear documentation header with license
 | 
			
		||||
2. List any dependencies
 | 
			
		||||
3. Provide configuration examples
 | 
			
		||||
4. Test on different systems
 | 
			
		||||
 | 
			
		||||
See existing extensions in `~/.local/share/ttyverse/extensions/` for examples.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
**See also:** [Extensions](Extensions) | [Configuration](Configuration) | [Commands Reference](Commands-Reference)
 | 
			
		||||
 | 
			
		||||
*Last updated: 2025-07-29*
 | 
			
		||||
							
								
								
									
										181
									
								
								Extensions.md
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
										
											
											
										
										
									
								
							
						
						
									
										181
									
								
								Extensions.md
									
									
									
									
									
								
							@@ -1,98 +1,177 @@
 | 
			
		||||
# Extensions
 | 
			
		||||
 | 
			
		||||
TTYverse supports extensions that add functionality like sound notifications, text-to-speech, and desktop integration. Extensions are Perl scripts that integrate with TTYverse's multi-process architecture.
 | 
			
		||||
TTYverse supports extensions that add functionality like sound notifications, text-to-speech, and desktop integration. Extensions enhance your fediverse experience with customizable features.
 | 
			
		||||
 | 
			
		||||
## Available Extensions
 | 
			
		||||
 | 
			
		||||
TTYverse includes several extensions that enhance the user experience:
 | 
			
		||||
TTYverse includes several built-in extensions:
 | 
			
		||||
 | 
			
		||||
- **Sound Pack** - Audio notifications for different types of posts and events
 | 
			
		||||
- **Text-to-Speech** - Speak posts and notifications aloud  
 | 
			
		||||
- **Desktop Notifications** - Visual notification bubbles
 | 
			
		||||
- **Timestamp Enhancement** - Enhanced timestamp display options
 | 
			
		||||
### Sound Pack Extension
 | 
			
		||||
- **Purpose**: Audio notifications for different types of posts and events
 | 
			
		||||
- **Sounds**: Default timeline, mentions, DMs, boosts, follows, polls, announcements
 | 
			
		||||
- **Configuration**: Customizable sound packs and audio commands
 | 
			
		||||
 | 
			
		||||
Extensions are included with TTYverse but not enabled by default.
 | 
			
		||||
### Text-to-Speech Extension  
 | 
			
		||||
- **Purpose**: Speak posts and notifications aloud for accessibility
 | 
			
		||||
- **Engines**: espeak, festival, pico, cepstral, macOS say
 | 
			
		||||
- **Features**: Configurable voice, speed, and language settings
 | 
			
		||||
 | 
			
		||||
## Extension Installation
 | 
			
		||||
### Desktop Notifications Extension
 | 
			
		||||
- **Purpose**: Visual notification bubbles on Linux/Unix desktops
 | 
			
		||||
- **Requirements**: libnotify and Gtk2::Notify Perl module
 | 
			
		||||
- **Integration**: Works with GNOME, KDE, and other desktop environments
 | 
			
		||||
 | 
			
		||||
### Extension Availability
 | 
			
		||||
TTYverse includes core extensions in the installation:
 | 
			
		||||
### Timestamp Extension
 | 
			
		||||
- **Purpose**: Enhanced timestamp display with time gaps
 | 
			
		||||
- **Features**: Shows timestamps when 5+ minutes pass between posts
 | 
			
		||||
 | 
			
		||||
## Installing Extensions
 | 
			
		||||
 | 
			
		||||
### Using the Extension Manager
 | 
			
		||||
 | 
			
		||||
TTYverse includes a management script for easy extension control:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
** attempting to load extensions
 | 
			
		||||
** loading /home/user/.local/share/ttyverse/extensions/extensionname.pl
 | 
			
		||||
** loaded extensions: extensionname
 | 
			
		||||
# Show available extensions
 | 
			
		||||
./extensions/manage-extensions.sh list
 | 
			
		||||
 | 
			
		||||
# Enable an extension
 | 
			
		||||
./extensions/manage-extensions.sh enable soundpack
 | 
			
		||||
 | 
			
		||||
# Disable an extension  
 | 
			
		||||
./extensions/manage-extensions.sh disable soundpack
 | 
			
		||||
 | 
			
		||||
# Check extension status
 | 
			
		||||
./extensions/manage-extensions.sh status soundpack
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Extension Location
 | 
			
		||||
Extensions are stored in:
 | 
			
		||||
### Manual Installation
 | 
			
		||||
 | 
			
		||||
Extensions are stored in the XDG data directory:
 | 
			
		||||
```
 | 
			
		||||
~/.local/share/ttyverse/extensions/
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Load extensions when starting TTYverse:
 | 
			
		||||
```bash
 | 
			
		||||
./ttyverse.pl -exts=soundpack,tts,libnotifyperl
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Configuration
 | 
			
		||||
 | 
			
		||||
Extensions are configured through TTYverse settings:
 | 
			
		||||
### Extension Settings
 | 
			
		||||
Configure extensions in your TTYverse RC file (`~/.config/ttyverse/ttyverserc`):
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# View extension-related settings
 | 
			
		||||
/get sound_enabled
 | 
			
		||||
/get tts_enabled
 | 
			
		||||
# Load extensions automatically
 | 
			
		||||
exts=soundpack,tts
 | 
			
		||||
 | 
			
		||||
# Enable/disable extensions
 | 
			
		||||
/set sound_enabled 1
 | 
			
		||||
/set extension_name 0
 | 
			
		||||
# Enable sound notifications
 | 
			
		||||
notifytype=soundpack
 | 
			
		||||
notifies=default,mention,dm,me,follow,boost,favourite
 | 
			
		||||
 | 
			
		||||
# Sound pack settings
 | 
			
		||||
extpref_sound_command=paplay
 | 
			
		||||
extpref_soundpack=default
 | 
			
		||||
 | 
			
		||||
# Text-to-speech settings
 | 
			
		||||
extpref_tts_synthesizer=espeak
 | 
			
		||||
extpref_tts_language=en-US
 | 
			
		||||
extpref_tts_rate=175
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Configuration can also be edited directly in:
 | 
			
		||||
```
 | 
			
		||||
~/.config/ttyverse/ttyverse_rc
 | 
			
		||||
### Runtime Configuration
 | 
			
		||||
Some settings can be changed while TTYverse is running:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# Toggle text-to-speech
 | 
			
		||||
/tts
 | 
			
		||||
 | 
			
		||||
# Get TTS help
 | 
			
		||||
/tts help
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Extension Dependencies
 | 
			
		||||
 | 
			
		||||
Some extensions may require additional system packages. Check the documentation for your specific distribution to install:
 | 
			
		||||
### Sound Pack Extension
 | 
			
		||||
- **Audio system**: PulseAudio (paplay), ALSA (aplay), or similar
 | 
			
		||||
- **Sound files**: OGG Vorbis format recommended
 | 
			
		||||
- **Commands**: `paplay`, `play` (SoX), `ogg123`, or `mpv`
 | 
			
		||||
 | 
			
		||||
- **Audio support** - PulseAudio or similar audio system
 | 
			
		||||
- **Text-to-speech** - TTS engines like espeak or festival
 | 
			
		||||
- **Desktop notifications** - libnotify and related Perl modules
 | 
			
		||||
### Text-to-Speech Extension
 | 
			
		||||
- **Synthesizers**: `espeak` (recommended), `festival`, `pico2wave`
 | 
			
		||||
- **Commercial**: Cepstral Swift (Linux), macOS `say` command
 | 
			
		||||
- **Languages**: Depends on synthesizer (espeak supports many)
 | 
			
		||||
 | 
			
		||||
## Custom Extensions
 | 
			
		||||
### Desktop Notifications Extension
 | 
			
		||||
- **Perl module**: `Gtk2::Notify`
 | 
			
		||||
- **System**: libnotify (`libnotify-dev` or similar package)
 | 
			
		||||
- **Desktop**: GUI environment with `DISPLAY` variable set
 | 
			
		||||
 | 
			
		||||
### Creating Extensions
 | 
			
		||||
Extensions are Perl scripts that follow TTYverse conventions. They can:
 | 
			
		||||
Install dependencies on common distributions:
 | 
			
		||||
 | 
			
		||||
- Hook into post processing
 | 
			
		||||
- React to different notification types  
 | 
			
		||||
- Add new commands
 | 
			
		||||
- Integrate with system features
 | 
			
		||||
```bash
 | 
			
		||||
# Debian/Ubuntu
 | 
			
		||||
sudo apt install libgtk2-notify-perl espeak pulseaudio-utils
 | 
			
		||||
 | 
			
		||||
### Extension Development
 | 
			
		||||
See the existing extensions in `~/.local/share/ttyverse/extensions/` for examples of how to create custom functionality.
 | 
			
		||||
# Fedora/RHEL  
 | 
			
		||||
sudo dnf install perl-Gtk2-Notify espeak pulseaudio-utils
 | 
			
		||||
 | 
			
		||||
# Arch Linux
 | 
			
		||||
sudo pacman -S perl-gtk2-notify espeak pulseaudio
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Troubleshooting
 | 
			
		||||
 | 
			
		||||
### Extension Issues
 | 
			
		||||
### Extension Loading Issues
 | 
			
		||||
```bash
 | 
			
		||||
# Check if extensions are loading
 | 
			
		||||
./ttyverse.pl -verbose
 | 
			
		||||
# Check if extensions are loading properly
 | 
			
		||||
./ttyverse.pl -exts=soundpack -verbose
 | 
			
		||||
 | 
			
		||||
# Test extension files manually  
 | 
			
		||||
perl ~/.local/share/ttyverse/extensions/extensionname.pl
 | 
			
		||||
# Test extension syntax
 | 
			
		||||
perl -c ~/.local/share/ttyverse/extensions/soundpack.pl
 | 
			
		||||
 | 
			
		||||
# Fix permissions if needed
 | 
			
		||||
chmod +x ~/.local/share/ttyverse/extensions/*.pl
 | 
			
		||||
# Check extension manager status
 | 
			
		||||
./extensions/manage-extensions.sh list
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Common Problems
 | 
			
		||||
- **Permission denied** - Make extension files executable
 | 
			
		||||
- **Missing dependencies** - Install required system packages
 | 
			
		||||
- **Configuration errors** - Check settings in ttyverse_rc
 | 
			
		||||
 | 
			
		||||
For specific extension issues, check the [Troubleshooting](Troubleshooting) guide.
 | 
			
		||||
**Extension not loading**
 | 
			
		||||
- Check that the extension file exists in `~/.local/share/ttyverse/extensions/`
 | 
			
		||||
- Verify the extension is enabled: `./extensions/manage-extensions.sh status extensionname`
 | 
			
		||||
- Look for Perl syntax errors: `perl -c extensionname.pl`
 | 
			
		||||
 | 
			
		||||
**No sound notifications**
 | 
			
		||||
- Test audio system: `paplay /usr/share/sounds/alsa/Front_Left.wav`
 | 
			
		||||
- Check sound pack configuration in RC file
 | 
			
		||||
- Verify sound files exist in `~/.local/share/ttyverse/sounds/default/`
 | 
			
		||||
 | 
			
		||||
**Text-to-speech not working**
 | 
			
		||||
- Test synthesizer: `espeak "test message"`
 | 
			
		||||
- Check TTS configuration: `/tts help`
 | 
			
		||||
- Install required TTS engine packages
 | 
			
		||||
 | 
			
		||||
**Desktop notifications not appearing**
 | 
			
		||||
- Verify GUI environment: `echo $DISPLAY`
 | 
			
		||||
- Test notification system: `notify-send "Test"`
 | 
			
		||||
- Install libnotify Perl module
 | 
			
		||||
 | 
			
		||||
### Getting Help
 | 
			
		||||
 | 
			
		||||
For specific extension issues, check the [Troubleshooting](Troubleshooting) guide or examine the extension source code for configuration examples.
 | 
			
		||||
 | 
			
		||||
## Creating Custom Extensions
 | 
			
		||||
 | 
			
		||||
Want to create your own extensions? See the [Extension Development](Extension-Development) guide for detailed information on:
 | 
			
		||||
 | 
			
		||||
- Extension architecture and hooks
 | 
			
		||||
- Available functions and variables  
 | 
			
		||||
- Code examples and best practices
 | 
			
		||||
- Testing and distribution
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
**See also:** [Configuration](Configuration) | [Troubleshooting](Troubleshooting) | [Installation](Installation)
 | 
			
		||||
**See also:** [Extension Development](Extension-Development) | [Configuration](Configuration) | [Troubleshooting](Troubleshooting)
 | 
			
		||||
 | 
			
		||||
*Last updated: 2025-07-28*
 | 
			
		||||
*Last updated: 2025-07-29*
 | 
			
		||||
		Reference in New Issue
	
	Block a user