diff --git a/README.md b/README.md index 3b94f91..a4c6487 100644 --- a/README.md +++ b/README.md @@ -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. \ No newline at end of file diff --git a/libnotifyperl.pl b/libnotifyperl.pl new file mode 100644 index 0000000..b74c6c4 --- /dev/null +++ b/libnotifyperl.pl @@ -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; \ No newline at end of file diff --git a/manage-extensions.sh b/manage-extensions.sh new file mode 100755 index 0000000..c3c3b85 --- /dev/null +++ b/manage-extensions.sh @@ -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 [extension_name] + +Commands: + list Show available and enabled extensions + enable Enable an extension (symlink to XDG extensions directory) + disable Disable an extension (remove from XDG extensions directory) + status 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 " + exit 1 + fi + enable_extension "$2" + ;; + disable) + if [[ -z "$2" ]]; then + echo -e "${RED}Error:${NC} Extension name required" + echo "Usage: $0 disable " + exit 1 + fi + disable_extension "$2" + ;; + status) + if [[ -z "$2" ]]; then + echo -e "${RED}Error:${NC} Extension name required" + echo "Usage: $0 status " + 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 diff --git a/soundpack.pl b/soundpack.pl new file mode 100644 index 0000000..88bb5cc --- /dev/null +++ b/soundpack.pl @@ -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; diff --git a/sounds/.gitattributes b/sounds/.gitattributes new file mode 100644 index 0000000..93b9037 --- /dev/null +++ b/sounds/.gitattributes @@ -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 diff --git a/sounds/README.md b/sounds/README.md new file mode 100644 index 0000000..2f1544c --- /dev/null +++ b/sounds/README.md @@ -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 diff --git a/sounds/default/announcement.ogg b/sounds/default/announcement.ogg new file mode 100644 index 0000000..593923b --- /dev/null +++ b/sounds/default/announcement.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b9adc332c7e703dfcab4f4bed0c17894033e1d2757b8acc0d5d2a62d24710ea +size 16280 diff --git a/sounds/default/boost.ogg b/sounds/default/boost.ogg new file mode 100644 index 0000000..8bb430a --- /dev/null +++ b/sounds/default/boost.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82093752634e0eb09edab4419b41fbc91488eb0e6b502637a0e3bc9c78db71e8 +size 17645 diff --git a/sounds/default/default.ogg b/sounds/default/default.ogg new file mode 100644 index 0000000..c6fff41 --- /dev/null +++ b/sounds/default/default.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f67d699155c42515b5b018fff0e2f60f5cb7ab3e0a91c41f1e2fddb0e905b3f +size 9096 diff --git a/sounds/default/dm.ogg b/sounds/default/dm.ogg new file mode 100644 index 0000000..3862708 --- /dev/null +++ b/sounds/default/dm.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc42b81d371243d27eefde7710932d66a2de38d108564a08d547fa02f52107d2 +size 32195 diff --git a/sounds/default/favourite.ogg b/sounds/default/favourite.ogg new file mode 100644 index 0000000..bb04bba --- /dev/null +++ b/sounds/default/favourite.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8c8d33fb2801d8afafa48ca228bce144021f8f2c7fc97424c14b7ca1a3985eb +size 29631 diff --git a/sounds/default/follow.ogg b/sounds/default/follow.ogg new file mode 100644 index 0000000..287b51d --- /dev/null +++ b/sounds/default/follow.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4dbe0b2c58a5b5e040bb180a0ed1942c7186c1ac21230927f83aa76a458270c7 +size 23047 diff --git a/sounds/default/me.ogg b/sounds/default/me.ogg new file mode 100644 index 0000000..10a9ec8 --- /dev/null +++ b/sounds/default/me.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71f690acb5f46002eada0d4933155cb126e65b0359e3409585e3182a4ed97cad +size 18508 diff --git a/sounds/default/mention.ogg b/sounds/default/mention.ogg new file mode 100644 index 0000000..81b9a76 --- /dev/null +++ b/sounds/default/mention.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aee054b3162c11c87e142cfd812fd876592e0984727858b27ebb628e60ec904a +size 19928 diff --git a/sounds/default/poll.ogg b/sounds/default/poll.ogg new file mode 100644 index 0000000..8aaefa4 --- /dev/null +++ b/sounds/default/poll.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81172783f40658df37036905a3e9cad85e9c8c37e17be82bcb5c9963d8dcd9ff +size 40235 diff --git a/timestamp.pl b/timestamp.pl new file mode 100644 index 0000000..f40a31c --- /dev/null +++ b/timestamp.pl @@ -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; +}; \ No newline at end of file diff --git a/tts.pl b/tts.pl new file mode 100644 index 0000000..d3f7f6d --- /dev/null +++ b/tts.pl @@ -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 = ; + 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 = ; + 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; +}; \ No newline at end of file