Updated extensions information.

Storm Dragon
2025-07-29 11:28:28 -04:00
parent 8e18c502b7
commit 50839c5880
2 changed files with 706 additions and 51 deletions

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*

@ -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*