Updated extensions.
This commit is contained in:
80
README.md
80
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.
|
70
libnotifyperl.pl
Normal file
70
libnotifyperl.pl
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# TTYverse Desktop Notifications Extension (libnotify-perl)
|
||||||
|
# Adapted from original TTYtter extension
|
||||||
|
# Provides desktop notifications using GTK Gtk2::Notify
|
||||||
|
# Published under the Floodgap Free Software License: http://www.floodgap.com/software/ffsl/
|
||||||
|
|
||||||
|
# What is it?
|
||||||
|
# This extension enables desktop notifications for TTYverse using the
|
||||||
|
# libnotify system on Linux/Unix with GUI environments.
|
||||||
|
|
||||||
|
# Requirements:
|
||||||
|
# - Gtk2::Notify Perl module
|
||||||
|
# - GUI environment with DISPLAY set
|
||||||
|
# - libnotify system installed
|
||||||
|
|
||||||
|
# Installation:
|
||||||
|
# Install dependency: cpan Gtk2::Notify
|
||||||
|
# Or on Debian/Ubuntu: sudo apt install libgtk2-notify-perl
|
||||||
|
# Or on Fedora/RHEL: sudo dnf install perl-Gtk2-Notify
|
||||||
|
# Or on Arch: sudo pacman -S perl-gtk2-notify
|
||||||
|
|
||||||
|
eval { require Gtk2::Notify };
|
||||||
|
if($@){
|
||||||
|
print "-- TTYverse notification extension requires Gtk2::Notify\n";
|
||||||
|
print "-- Install with: cpan Gtk2::Notify\n";
|
||||||
|
print "-- Or on Debian/Ubuntu: sudo apt install libgtk2-notify-perl\n";
|
||||||
|
print "-- Or on Fedora/RHEL: sudo dnf install perl-Gtk2-Notify\n";
|
||||||
|
print "-- Or on Arch: sudo pacman -S perl-gtk2-notify\n";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub notifier_libnotifyperl {
|
||||||
|
# Skip notifications if no GUI display available
|
||||||
|
return 1 if(!$ENV{'DISPLAY'});
|
||||||
|
|
||||||
|
my $class = shift;
|
||||||
|
my $text = shift;
|
||||||
|
my $ref = shift; # Post reference data (unused in this version)
|
||||||
|
|
||||||
|
# Handle initialization
|
||||||
|
if (!defined($class) || !defined($notify_send_path)) {
|
||||||
|
if (!defined($class)) {
|
||||||
|
return 1 if ($script); # Skip in script mode
|
||||||
|
$class = 'TTYverse Desktop Notifications';
|
||||||
|
$text = 'Desktop notifications are now active for TTYverse.';
|
||||||
|
Gtk2::Notify->init('ttyverse');
|
||||||
|
print $streamout "-- $text\n" if (!$silent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show notification if system is properly initialized
|
||||||
|
if (Gtk2::Notify->is_initted()) {
|
||||||
|
my $notification = Gtk2::Notify->new(
|
||||||
|
"TTYverse: $class",
|
||||||
|
$text
|
||||||
|
);
|
||||||
|
|
||||||
|
# Set reasonable timeout (5 seconds)
|
||||||
|
$notification->set_timeout(5_000);
|
||||||
|
|
||||||
|
if($notification->show()) {
|
||||||
|
# Debug output (optional)
|
||||||
|
# print $streamout "-- Notification shown: $class\n" if ($debug);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make sure the module loads properly
|
||||||
|
1;
|
287
manage-extensions.sh
Executable file
287
manage-extensions.sh
Executable file
@ -0,0 +1,287 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# TTYverse Extension Manager
|
||||||
|
# Simple XDG-compliant extension management for ~/.local/share/ttyverse/extensions/
|
||||||
|
#
|
||||||
|
|
||||||
|
# XDG data directory for TTYverse
|
||||||
|
DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/ttyverse"
|
||||||
|
EXT_DIR="$DATA_DIR/extensions"
|
||||||
|
BUNDLED_DIR="$(dirname "$0")"
|
||||||
|
|
||||||
|
# Colors for output (only if stdout is a terminal)
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m'
|
||||||
|
else
|
||||||
|
GREEN=''
|
||||||
|
YELLOW=''
|
||||||
|
RED=''
|
||||||
|
NC=''
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure extension directory exists
|
||||||
|
mkdir -p "$EXT_DIR"
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
cat << EOF
|
||||||
|
TTYverse Extension Manager
|
||||||
|
|
||||||
|
Usage: $0 <command> [extension_name]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
list Show available and enabled extensions
|
||||||
|
enable <ext> Enable an extension (symlink to XDG extensions directory)
|
||||||
|
disable <ext> Disable an extension (remove from XDG extensions directory)
|
||||||
|
status <ext> Show status of specific extension
|
||||||
|
help Show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$0 list # Show all extensions
|
||||||
|
$0 enable soundpack # Enable soundpack extension
|
||||||
|
$0 disable soundpack # Disable soundpack extension
|
||||||
|
$0 status soundpack # Check if soundpack is enabled
|
||||||
|
|
||||||
|
XDG Extensions Directory: $EXT_DIR
|
||||||
|
Bundled Extensions: $BUNDLED_DIR
|
||||||
|
|
||||||
|
Note: TTYverse only loads extensions from the XDG directory.
|
||||||
|
Use this script to enable/disable bundled extensions or install your own.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
list_extensions() {
|
||||||
|
echo -e "${GREEN}Available Extensions:${NC}"
|
||||||
|
for ext in "$BUNDLED_DIR"/*.pl; do
|
||||||
|
if [[ -f "$ext" ]]; then
|
||||||
|
basename="${ext##*/}"
|
||||||
|
name="${basename%.pl}"
|
||||||
|
if [[ -L "$EXT_DIR/$basename" || -f "$EXT_DIR/$basename" ]]; then
|
||||||
|
echo -e " ${GREEN}✓${NC} $name (enabled)"
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}○${NC} $name (available)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "\n${GREEN}User Extensions:${NC}"
|
||||||
|
for ext in "$EXT_DIR"/*.pl; do
|
||||||
|
if [[ -f "$ext" && ! -L "$ext" ]]; then
|
||||||
|
basename="${ext##*/}"
|
||||||
|
name="${basename%.pl}"
|
||||||
|
echo -e " ${GREEN}●${NC} $name (user-installed)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_extension() {
|
||||||
|
local name="$1"
|
||||||
|
local ext_file="$name.pl"
|
||||||
|
local bundled_path="$BUNDLED_DIR/$ext_file"
|
||||||
|
local user_path="$EXT_DIR/$ext_file"
|
||||||
|
|
||||||
|
if [[ ! -f "$bundled_path" ]]; then
|
||||||
|
echo -e "${RED}Error:${NC} Extension '$name' not found in bundled extensions"
|
||||||
|
echo "Available extensions:"
|
||||||
|
for ext in "$BUNDLED_DIR"/*.pl; do
|
||||||
|
if [[ -f "$ext" ]]; then
|
||||||
|
basename="${ext##*/}"
|
||||||
|
echo " ${basename%.pl}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -L "$user_path" ]]; then
|
||||||
|
echo -e "${YELLOW}Warning:${NC} Extension '$name' is already enabled"
|
||||||
|
return 0
|
||||||
|
elif [[ -f "$user_path" ]]; then
|
||||||
|
echo -e "${YELLOW}Warning:${NC} User-installed extension '$name' already exists"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ln -s "$(realpath "$bundled_path")" "$user_path"
|
||||||
|
echo -e "${GREEN}✓${NC} Enabled extension: $name"
|
||||||
|
echo " Linked: $bundled_path -> $user_path"
|
||||||
|
|
||||||
|
# Special handling for soundpack extension - copy default sounds and configure RC
|
||||||
|
if [[ "$name" == "soundpack" ]]; then
|
||||||
|
local sounds_src="$BUNDLED_DIR/sounds"
|
||||||
|
local sounds_dst="$DATA_DIR/sounds"
|
||||||
|
local config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/ttyverse"
|
||||||
|
local rc_file="$config_dir/ttyverserc"
|
||||||
|
|
||||||
|
# Copy sound files
|
||||||
|
if [[ -d "$sounds_src" ]]; then
|
||||||
|
echo " Copying bundled sound files..."
|
||||||
|
cp -r "$sounds_src"/* "$sounds_dst/" 2>/dev/null || true
|
||||||
|
echo -e "${GREEN}✓${NC} Copied default sound pack to $sounds_dst/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Auto-configure RC file
|
||||||
|
mkdir -p "$config_dir"
|
||||||
|
local needs_config=false
|
||||||
|
|
||||||
|
# Check if soundpack extension is already configured
|
||||||
|
# Need to check for exts, notifytype, and notifies all being properly set
|
||||||
|
local has_exts=false
|
||||||
|
local has_notifytype=false
|
||||||
|
local has_notifies=false
|
||||||
|
|
||||||
|
if [[ -f "$rc_file" ]]; then
|
||||||
|
grep -q "^exts.*soundpack" "$rc_file" 2>/dev/null && has_exts=true
|
||||||
|
grep -q "^notifytype=.*soundpack" "$rc_file" 2>/dev/null && has_notifytype=true
|
||||||
|
grep -q "^notifies=.*mention.*boost.*favourite" "$rc_file" 2>/dev/null && has_notifies=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$has_exts" == "false" ]] || [[ "$has_notifytype" == "false" ]] || [[ "$has_notifies" == "false" ]]; then
|
||||||
|
needs_config=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$needs_config" == "true" ]]; then
|
||||||
|
echo " Configuring TTYverse for sound notifications..."
|
||||||
|
|
||||||
|
# Create backup if RC file exists
|
||||||
|
[[ -f "$rc_file" ]] && cp "$rc_file" "$rc_file.backup.$(date +%s)"
|
||||||
|
|
||||||
|
# Update or create exts line
|
||||||
|
if [[ -f "$rc_file" ]] && grep -q "^exts=" "$rc_file"; then
|
||||||
|
# Check if soundpack is already in exts line
|
||||||
|
if ! grep -q "^exts=.*soundpack" "$rc_file"; then
|
||||||
|
sed -i 's/^exts=\(.*\)/exts=\1,soundpack/' "$rc_file"
|
||||||
|
echo " Added soundpack to existing exts line"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Add new exts line
|
||||||
|
echo "exts=soundpack" >> "$rc_file"
|
||||||
|
echo " Added exts=soundpack"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add or update notifytype
|
||||||
|
if [[ -f "$rc_file" ]] && grep -q "^notifytype=" "$rc_file"; then
|
||||||
|
sed -i 's/^notifytype=.*/notifytype=soundpack/' "$rc_file"
|
||||||
|
echo " Updated notifytype to soundpack"
|
||||||
|
else
|
||||||
|
echo "notifytype=soundpack" >> "$rc_file"
|
||||||
|
echo " Added notifytype=soundpack"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add or update notifies parameter for sound events
|
||||||
|
if [[ -f "$rc_file" ]] && grep -q "^notifies=" "$rc_file"; then
|
||||||
|
# Update existing notifies line to include fediverse sound types
|
||||||
|
sed -i 's/^notifies=.*/notifies=default,mention,dm,me,follow,boost,favourite/' "$rc_file"
|
||||||
|
echo " Updated existing notifies configuration"
|
||||||
|
else
|
||||||
|
# Add new notifies line
|
||||||
|
echo "notifies=default,mention,dm,me,follow,boost,favourite" >> "$rc_file"
|
||||||
|
echo " Added notifies configuration"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add extension preferences
|
||||||
|
{
|
||||||
|
echo ""
|
||||||
|
echo "# Sound notifications (added by extension manager)"
|
||||||
|
echo "extpref_sound_command=paplay"
|
||||||
|
echo "extpref_soundpack=default"
|
||||||
|
echo ""
|
||||||
|
echo "# Fediverse sound types: default, mention, dm, me, follow, boost, favourite, poll, announcement"
|
||||||
|
} >> "$rc_file"
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓${NC} Configured TTYverse RC file for sound notifications"
|
||||||
|
echo " Restart TTYverse to enable sounds automatically"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Note:${NC} Soundpack already configured in RC file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_extension() {
|
||||||
|
local name="$1"
|
||||||
|
local ext_file="$name.pl"
|
||||||
|
local user_path="$EXT_DIR/$ext_file"
|
||||||
|
|
||||||
|
if [[ ! -e "$user_path" ]]; then
|
||||||
|
echo -e "${YELLOW}Warning:${NC} Extension '$name' is not enabled"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -L "$user_path" ]]; then
|
||||||
|
rm "$user_path"
|
||||||
|
echo -e "${GREEN}✓${NC} Disabled extension: $name"
|
||||||
|
|
||||||
|
# Special handling for soundpack - offer to remove from RC file
|
||||||
|
if [[ "$name" == "soundpack" ]]; then
|
||||||
|
local config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/ttyverse"
|
||||||
|
local rc_file="$config_dir/ttyverserc"
|
||||||
|
|
||||||
|
if [[ -f "$rc_file" ]] && grep -q "soundpack" "$rc_file"; then
|
||||||
|
echo -e "${YELLOW}Note:${NC} Soundpack is still configured in $rc_file"
|
||||||
|
echo " You may want to remove the soundpack lines to fully disable"
|
||||||
|
echo " Or run TTYverse without -exts=soundpack to override"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [[ -f "$user_path" ]]; then
|
||||||
|
echo -e "${YELLOW}Warning:${NC} '$name' is a user-installed extension (not a symlink)"
|
||||||
|
echo "Remove manually if needed: $user_path"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_status() {
|
||||||
|
local name="$1"
|
||||||
|
local ext_file="$name.pl"
|
||||||
|
local bundled_path="$BUNDLED_DIR/$ext_file"
|
||||||
|
local user_path="$EXT_DIR/$ext_file"
|
||||||
|
|
||||||
|
echo "Extension: $name"
|
||||||
|
echo "Bundled: $([[ -f "$bundled_path" ]] && echo "Available" || echo "Not found")"
|
||||||
|
|
||||||
|
if [[ -L "$user_path" ]]; then
|
||||||
|
echo -e "Status: ${GREEN}Enabled${NC} (symlinked)"
|
||||||
|
echo "Target: $(readlink "$user_path")"
|
||||||
|
elif [[ -f "$user_path" ]]; then
|
||||||
|
echo -e "Status: ${GREEN}Enabled${NC} (user-installed)"
|
||||||
|
else
|
||||||
|
echo -e "Status: ${YELLOW}Disabled${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main command handling
|
||||||
|
case "${1:-help}" in
|
||||||
|
list|ls)
|
||||||
|
list_extensions
|
||||||
|
;;
|
||||||
|
enable)
|
||||||
|
if [[ -z "$2" ]]; then
|
||||||
|
echo -e "${RED}Error:${NC} Extension name required"
|
||||||
|
echo "Usage: $0 enable <extension_name>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
enable_extension "$2"
|
||||||
|
;;
|
||||||
|
disable)
|
||||||
|
if [[ -z "$2" ]]; then
|
||||||
|
echo -e "${RED}Error:${NC} Extension name required"
|
||||||
|
echo "Usage: $0 disable <extension_name>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
disable_extension "$2"
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
if [[ -z "$2" ]]; then
|
||||||
|
echo -e "${RED}Error:${NC} Extension name required"
|
||||||
|
echo "Usage: $0 status <extension_name>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
show_status "$2"
|
||||||
|
;;
|
||||||
|
help|--help|-h)
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${RED}Error:${NC} Unknown command: $1"
|
||||||
|
echo "Run '$0 help' for usage information"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
76
soundpack.pl
Normal file
76
soundpack.pl
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# TTYverse Sound Pack Extension
|
||||||
|
# Adapted from the original TTYtter soundpack by Storm Dragon: https://stormux.org/
|
||||||
|
# Support Storm Dragon's work: https://patreon.com/stormux
|
||||||
|
# Published under the Floodgap Free Software License: http://www.floodgap.com/software/ffsl/
|
||||||
|
|
||||||
|
# Plays sounds for different fediverse activities:
|
||||||
|
# Core: default, mention, dm, me
|
||||||
|
# Fediverse: follow, boost, favourite, poll, announcement
|
||||||
|
#
|
||||||
|
# Configure in RC file with:
|
||||||
|
# extpref_sound_command=paplay # Sound command (paplay, play, ogg123, etc.)
|
||||||
|
# extpref_soundpack=default # Sound pack name
|
||||||
|
# notifytype=soundpack # Enable sound notifications
|
||||||
|
# notifies=default,mention,dm,me,follow,boost,favourite
|
||||||
|
|
||||||
|
sub notifier_soundpack {
|
||||||
|
my $class = shift;
|
||||||
|
my $text = shift;
|
||||||
|
my $ref = shift;
|
||||||
|
|
||||||
|
# Debug output
|
||||||
|
print $stdout "-- DEBUG: soundpack notifier called with class='$class'\n" if (defined($class) && $verbose);
|
||||||
|
|
||||||
|
# Determine sound command (default to paplay for modern Linux)
|
||||||
|
my $sound_command;
|
||||||
|
if (!$extpref_sound_command) {
|
||||||
|
$sound_command = "paplay"; # Modern default for PulseAudio
|
||||||
|
} else {
|
||||||
|
$sound_command = $extpref_sound_command;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine sound pack name
|
||||||
|
my $sound_pack;
|
||||||
|
if (!$extpref_soundpack) {
|
||||||
|
$sound_pack = "default";
|
||||||
|
} else {
|
||||||
|
$sound_pack = $extpref_soundpack;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Skip if silent mode enabled
|
||||||
|
return 1 if ($silent);
|
||||||
|
|
||||||
|
if (!defined($class)) {
|
||||||
|
# Initialize - show loaded message
|
||||||
|
print $stdout "-- TTYverse sound pack loaded: $sound_pack\n";
|
||||||
|
print $stdout "-- Supported sounds: default, mention, dm, me, follow, boost, favourite, poll, announcement\n" if (!$silent);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build sound file path using XDG data directory
|
||||||
|
my $data_dir = "$ENV{'HOME'}/.local/share/ttyverse";
|
||||||
|
my $sound_file = "$data_dir/sounds/$sound_pack/" . lc($class) . ".ogg";
|
||||||
|
|
||||||
|
if (!-f $sound_file) {
|
||||||
|
# Only warn once per session per class
|
||||||
|
my $warn_key = "warned_missing_sound_$class";
|
||||||
|
if (!$store->{$warn_key}) {
|
||||||
|
print $stdout "-- Warning: Sound file '$class.ogg' not found in pack '$sound_pack'\n";
|
||||||
|
print $stdout "-- Place sound files in $data_dir/sounds/$sound_pack/\n";
|
||||||
|
$store->{$warn_key} = 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Play the sound in background (suppress output)
|
||||||
|
print $stdout "-- DEBUG: Playing sound: $sound_command \"$sound_file\"\n" if ($verbose);
|
||||||
|
system("$sound_command \"$sound_file\" >/dev/null 2>&1 &");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Mark extension as successfully loaded
|
||||||
|
$store->{'loaded'} = 1;
|
||||||
|
|
||||||
|
# Extension loaded successfully
|
||||||
|
1;
|
4
sounds/.gitattributes
vendored
Normal file
4
sounds/.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.opus filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
119
sounds/README.md
Normal file
119
sounds/README.md
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# TTYverse Sound Packs
|
||||||
|
|
||||||
|
TTYverse supports audio notifications for different types of fediverse activity. Sound packs are collections of audio files that play when specific events occur.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. **Enable sounds**: Use the extension manager to enable the soundpack extension:
|
||||||
|
```bash
|
||||||
|
./extensions/manage-extensions.sh enable soundpack
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configure TTYverse**: Add to your `~/.config/ttyverse/ttyverserc`:
|
||||||
|
```
|
||||||
|
notifytype=soundpack
|
||||||
|
notifies=default,mention,dm,me,search,follow,boost,favourite
|
||||||
|
extpref_soundpack=default
|
||||||
|
extpref_sound_command=paplay
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Test**: Start TTYverse and you should hear sounds for different activities!
|
||||||
|
|
||||||
|
## Sound Pack Structure
|
||||||
|
|
||||||
|
Sound packs are stored in `~/.local/share/ttyverse/sounds/` with this structure:
|
||||||
|
```
|
||||||
|
sounds/
|
||||||
|
├── default/ # Default sound pack (included)
|
||||||
|
│ ├── default.ogg # Regular posts
|
||||||
|
│ ├── mention.ogg # @mentions of you
|
||||||
|
│ ├── dm.ogg # Direct messages
|
||||||
|
│ ├── me.ogg # Your own posts
|
||||||
|
│ ├── follow.ogg # New followers
|
||||||
|
│ ├── boost.ogg # Your posts boosted
|
||||||
|
│ ├── favourite.ogg # Your posts favourited
|
||||||
|
│ ├── poll.ogg # Poll notifications
|
||||||
|
│ └── announcement.ogg # Server announcements
|
||||||
|
├── custom/ # Your custom sound pack
|
||||||
|
│ └── *.ogg
|
||||||
|
└── README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required Sound Files
|
||||||
|
|
||||||
|
### Core Sounds (Essential)
|
||||||
|
- **`default.ogg`** - Regular timeline posts
|
||||||
|
- **`mention.ogg`** - When someone @mentions you
|
||||||
|
- **`dm.ogg`** - Direct messages received
|
||||||
|
- **`me.ogg`** - Your own posts (optional feedback)
|
||||||
|
|
||||||
|
### Fediverse Interaction Sounds
|
||||||
|
- **`follow.ogg`** - Someone follows you
|
||||||
|
- **`boost.ogg`** - Someone boosts (shares) your post
|
||||||
|
- **`favourite.ogg`** - Someone favourites (likes) your post
|
||||||
|
- **`poll.ogg`** - Poll results or poll you voted in ends
|
||||||
|
- **`announcement.ogg`** - Server announcements
|
||||||
|
|
||||||
|
## Creating Custom Sound Packs
|
||||||
|
|
||||||
|
1. **Create directory**: Make a new directory under `sounds/` with your pack name:
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.local/share/ttyverse/sounds/mystyle
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Add sound files**: Place `.ogg` files with the required names. All files should be:
|
||||||
|
- **Format**: OGG Vorbis (most compatible)
|
||||||
|
- **Length**: 1-3 seconds recommended (short and sweet)
|
||||||
|
- **Volume**: Normalized to prevent startling users
|
||||||
|
- **Sample Rate**: 44.1kHz or 48kHz
|
||||||
|
|
||||||
|
3. **Configure TTYverse**: Update your config to use the new pack:
|
||||||
|
```
|
||||||
|
extpref_soundpack=mystyle
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Test**: Restart TTYverse to load the new sounds
|
||||||
|
|
||||||
|
## Audio Format Support
|
||||||
|
|
||||||
|
TTYverse uses your system's audio command for playback. Supported formats depend on your audio player:
|
||||||
|
|
||||||
|
- **paplay** (PulseAudio default): OGG, WAV, FLAC
|
||||||
|
- **play** (SoX): Most formats including MP3, OGG, WAV
|
||||||
|
- **ogg123**: OGG Vorbis only
|
||||||
|
- **mpv**: Most formats including MP3, OGG, WAV, FLAC
|
||||||
|
|
||||||
|
Configure your preferred player with:
|
||||||
|
```
|
||||||
|
extpref_sound_command=paplay # or play, ogg123, mpv, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### No Sound
|
||||||
|
1. Check audio command: `paplay /usr/share/sounds/alsa/Front_Left.wav`
|
||||||
|
2. Verify sound pack path: `ls ~/.local/share/ttyverse/sounds/`
|
||||||
|
3. Check TTYverse config: `grep -E "(notifytype|soundpack)" ~/.config/ttyverse/ttyverserc`
|
||||||
|
4. Test with verbose mode: TTYverse will show warnings for missing sounds
|
||||||
|
|
||||||
|
### Wrong Sounds Playing
|
||||||
|
- Verify file names exactly match the required names (case-sensitive)
|
||||||
|
- Check that you're using the correct sound pack name in config
|
||||||
|
- Restart TTYverse after changing sound files
|
||||||
|
|
||||||
|
### Performance Issues
|
||||||
|
- Use compressed formats like OGG instead of WAV
|
||||||
|
- Keep sound files under 3 seconds
|
||||||
|
- Use `&` in sound command for background playback (already handled by extension)
|
||||||
|
|
||||||
|
## Example Sound Pack Themes
|
||||||
|
|
||||||
|
### **Retro Gaming**
|
||||||
|
Use classic 8-bit style beeps and boops for different actions
|
||||||
|
|
||||||
|
### **Natural Sounds**
|
||||||
|
Bird calls, water drops, wind chimes for a calming experience
|
||||||
|
|
||||||
|
### **Minimal**
|
||||||
|
Simple tones with different pitches for each notification type
|
BIN
sounds/default/announcement.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/default/announcement.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/default/boost.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/default/boost.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/default/default.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/default/default.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/default/dm.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/default/dm.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/default/favourite.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/default/favourite.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/default/follow.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/default/follow.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/default/me.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/default/me.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/default/mention.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/default/mention.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/default/poll.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/default/poll.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
60
timestamp.pl
Normal file
60
timestamp.pl
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# TTYverse Timestamp Extension
|
||||||
|
# Modified by Storm Dragon: https://stormux.org/
|
||||||
|
# Support Storm Dragon's work: https://patreon.com/stormux
|
||||||
|
# Original concept from kosertech.com (see ttyverse.wiki/Extension-Development.md for development guide)
|
||||||
|
# Published under the Floodgap Free Software License: http://www.floodgap.com/software/ffsl/
|
||||||
|
|
||||||
|
# What is it?
|
||||||
|
# This extension adds timestamps before posts when 5 minutes or more have passed
|
||||||
|
# between timeline updates, helping organize the flow of your fediverse timeline.
|
||||||
|
|
||||||
|
# Usage:
|
||||||
|
# This extension works automatically. Load it when you start TTYverse or add it
|
||||||
|
# to your configuration. The timestamp format is: -- HH:MMAM/PM --
|
||||||
|
|
||||||
|
$handle = sub {
|
||||||
|
my $delayInSeconds = 60 * 5; # 5 minutes default
|
||||||
|
my ($nsec,$nmin,$nhour,$nday,$nmon,$nyear) = localtime(time);
|
||||||
|
|
||||||
|
# Initialize timestamp tracking on first run
|
||||||
|
if(!$Lib_firstrun){
|
||||||
|
$Lib_firstrun = 1;
|
||||||
|
print "-- TTYverse timestamp extension loaded\n" if (!$silent);
|
||||||
|
$Lib_past = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print timestamp if enough time has elapsed
|
||||||
|
if($Lib_past < (time - $delayInSeconds)){
|
||||||
|
$Lib_past = time;
|
||||||
|
my $timeString = "-- ";
|
||||||
|
my $timeOfDay = "AM";
|
||||||
|
|
||||||
|
# Convert to 12-hour format
|
||||||
|
if ($nhour >= 12) {
|
||||||
|
if ($nhour > 12) {
|
||||||
|
$nhour = $nhour - 12;
|
||||||
|
}
|
||||||
|
$timeOfDay = "PM";
|
||||||
|
}
|
||||||
|
if (($nhour < 10) && ($nhour != 0)) {
|
||||||
|
$timeString .= "0";
|
||||||
|
}
|
||||||
|
if ($nhour == 0) {
|
||||||
|
$nhour = 12;
|
||||||
|
$timeOfDay = "AM";
|
||||||
|
}
|
||||||
|
|
||||||
|
$timeString .= "$nhour:";
|
||||||
|
if ($nmin < 10) {
|
||||||
|
$timeString .= "0";
|
||||||
|
}
|
||||||
|
$timeString .= "$nmin$timeOfDay";
|
||||||
|
print $streamout "$timeString --\n" if (!$silent);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allow TTYverse to handle displaying the post normally
|
||||||
|
my $ref = shift;
|
||||||
|
&defaulthandle($ref);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
};
|
153
tts.pl
Normal file
153
tts.pl
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
# TTYverse Text-to-Speech Extension
|
||||||
|
# By Storm Dragon: https://stormux.org/
|
||||||
|
# Support Storm Dragon's work: https://patreon.com/stormux
|
||||||
|
# Published under the Floodgap Free Software License: http://www.floodgap.com/software/ffsl/
|
||||||
|
|
||||||
|
# What is it?
|
||||||
|
# This extension uses text-to-speech to read incoming fediverse posts.
|
||||||
|
# Multiple synthesizer engines are supported for accessibility.
|
||||||
|
|
||||||
|
# Usage:
|
||||||
|
# Toggle TTS on/off: /tts
|
||||||
|
# Get help: /tts help
|
||||||
|
# Load extension when starting TTYverse or add to configuration
|
||||||
|
|
||||||
|
$addaction = sub {
|
||||||
|
my $command = shift;
|
||||||
|
my $speak = "";
|
||||||
|
|
||||||
|
# Get current TTS state from config
|
||||||
|
my $tts_config = "$ENV{'HOME'}/.config/ttyverse/tts_enabled";
|
||||||
|
if (-e $tts_config) {
|
||||||
|
open TTSSETTINGS, $tts_config;
|
||||||
|
$speak = <TTSSETTINGS>;
|
||||||
|
close TTSSETTINGS;
|
||||||
|
chomp($speak) if defined($speak);
|
||||||
|
} else {
|
||||||
|
$speak = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($command eq '/tts') {
|
||||||
|
if ((!defined($speak)) || ($speak eq 1)) {
|
||||||
|
$speak = 0;
|
||||||
|
print $streamout "-- Text-to-speech disabled\n";
|
||||||
|
} else {
|
||||||
|
$speak = 1;
|
||||||
|
print $streamout "-- Text-to-speech enabled\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save new state
|
||||||
|
open TTSSETTINGS, ">$tts_config";
|
||||||
|
print TTSSETTINGS "$speak";
|
||||||
|
close TTSSETTINGS;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($command eq "/tts help") {
|
||||||
|
my $ttsHelp = "TTYverse Text-to-Speech Extension\n\n" .
|
||||||
|
"Commands:\n" .
|
||||||
|
" /tts - Toggle speech on/off\n" .
|
||||||
|
" /tts help - Show this help\n\n" .
|
||||||
|
"Configuration (add to TTYverse config):\n\n" .
|
||||||
|
"extpref_tts_synthesizer=synthName\n" .
|
||||||
|
" Available: espeak (default), festival, pico, cepstral, mac\n\n" .
|
||||||
|
"extpref_tts_language=languageCode\n" .
|
||||||
|
" Default: en-US (supported by espeak and pico)\n\n" .
|
||||||
|
"extpref_tts_rate=number\n" .
|
||||||
|
" Speech rate, default: 175 (espeak only)\n\n" .
|
||||||
|
"extpref_tts_variant=name\n" .
|
||||||
|
" Voice variant/name (espeak variants, cepstral/mac voices)\n";
|
||||||
|
print $streamout "$ttsHelp";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
$handle = sub {
|
||||||
|
my $ref = shift;
|
||||||
|
my $class = shift;
|
||||||
|
|
||||||
|
# Skip TTS during initial timeline load
|
||||||
|
if ($last_id eq 0) {
|
||||||
|
&defaulthandle($ref);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $speak = "";
|
||||||
|
# Get TTS state from config
|
||||||
|
my $tts_config = "$ENV{'HOME'}/.config/ttyverse/tts_enabled";
|
||||||
|
if (-e $tts_config) {
|
||||||
|
open TTSSETTINGS, $tts_config;
|
||||||
|
$speak = <TTSSETTINGS>;
|
||||||
|
close TTSSETTINGS;
|
||||||
|
chomp($speak) if defined($speak);
|
||||||
|
} else {
|
||||||
|
$speak = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Initialize TTS configuration
|
||||||
|
my $soundCommand = "paplay"; # Default for modern Linux/PulseAudio
|
||||||
|
my $synthesizer = $extpref_tts_synthesizer || "espeak";
|
||||||
|
my $language = $extpref_tts_language || "en-US";
|
||||||
|
my $variant = $extpref_tts_variant || "";
|
||||||
|
my $rate = $extpref_tts_rate || "";
|
||||||
|
|
||||||
|
# Override sound command if configured
|
||||||
|
if ($extpref_sound_command) {
|
||||||
|
$soundCommand = $extpref_sound_command;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract post content for TTS
|
||||||
|
my $postText = &descape($ref->{'account'}->{'display_name'} || $ref->{'account'}->{'username'}) .
|
||||||
|
" posted: " . &descape($ref->{'content'});
|
||||||
|
|
||||||
|
# Make speech more accessible
|
||||||
|
$postText =~ s/\"/ /g; # Remove quotes
|
||||||
|
$postText =~ s/ \#/ hashtag /g; # Make hashtags readable
|
||||||
|
$postText =~ s/https?:\/\/\S+/ link /g; # Simplify links
|
||||||
|
$postText =~ s/www\./ /g; # Remove www
|
||||||
|
$postText =~ s/\// forward slash /g; # Make slashes readable
|
||||||
|
$postText =~ s/\\/ back slash /g; # Make backslashes readable
|
||||||
|
$postText =~ s/\blol\b/ laughs out loud /gi; # Expand common abbreviations
|
||||||
|
$postText =~ s/\brofl\b/ rolling on the floor laughing /gi;
|
||||||
|
$postText =~ s/<[^>]*>//g; # Remove any remaining HTML
|
||||||
|
|
||||||
|
my $speechCommand = "";
|
||||||
|
|
||||||
|
# Configure synthesizer command
|
||||||
|
if ($synthesizer eq "cepstral") {
|
||||||
|
$speechCommand = "aoss swift ";
|
||||||
|
if ($variant ne "") {
|
||||||
|
$speechCommand .= "-n $variant ";
|
||||||
|
}
|
||||||
|
$speechCommand .= "\"$postText\"";
|
||||||
|
} elsif ($synthesizer eq "espeak") {
|
||||||
|
if ($rate eq "") {
|
||||||
|
$rate = "175";
|
||||||
|
}
|
||||||
|
$speechCommand = "$synthesizer -v " . lc($language);
|
||||||
|
if ($variant ne "") {
|
||||||
|
$speechCommand .= "+$variant";
|
||||||
|
}
|
||||||
|
$speechCommand .= " -s $rate \"$postText\"";
|
||||||
|
} elsif ($synthesizer eq "festival") {
|
||||||
|
$speechCommand = "echo \"$postText\" | festival --tts";
|
||||||
|
} elsif ($synthesizer eq "mac") {
|
||||||
|
$speechCommand = "say ";
|
||||||
|
if ($variant ne "") {
|
||||||
|
$speechCommand .= "-v $variant ";
|
||||||
|
}
|
||||||
|
$speechCommand .= "\"$postText\"";
|
||||||
|
} elsif ($synthesizer eq "pico") {
|
||||||
|
$speechCommand = "pico2wave -l $language -w /tmp/ttyverse.wav \"$postText\" && $soundCommand /tmp/ttyverse.wav";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Speak the post if TTS is enabled
|
||||||
|
if ($speak eq 1) {
|
||||||
|
system("$speechCommand");
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allow TTYverse to display the post normally
|
||||||
|
&defaulthandle($ref);
|
||||||
|
return 1;
|
||||||
|
};
|
Reference in New Issue
Block a user