From 50839c58801bad2e484aefb238edd388bfd5eedb Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Tue, 29 Jul 2025 11:28:28 -0400 Subject: [PATCH] Updated extensions information. --- Extension-Development.md | 576 +++++++++++++++++++++++++++++++++++++++ Extensions.md | 181 ++++++++---- 2 files changed, 706 insertions(+), 51 deletions(-) create mode 100644 Extension-Development.md diff --git a/Extension-Development.md b/Extension-Development.md new file mode 100644 index 0000000..1f2af99 --- /dev/null +++ b/Extension-Development.md @@ -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 HTML', + '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* \ No newline at end of file diff --git a/Extensions.md b/Extensions.md index f3df69e..dbb5c21 100644 --- a/Extensions.md +++ b/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* \ No newline at end of file +*Last updated: 2025-07-29* \ No newline at end of file