Test Notifications & Title Updates
All checks were successful
Build & Push Docker Image / build-and-publish (push) Successful in 21s

This commit is contained in:
2026-02-05 16:24:18 -05:00
parent 08b959d93e
commit dd5d9fc507
6 changed files with 341 additions and 1 deletions

74
90-start-services Normal file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/with-contenv bash
# Start notification bridge and window title sync services
# This runs during container initialization
echo "[init] Starting background services for notifications and window title sync"
# Create log directory
mkdir -p /config/logs
chown abc:abc /config/logs
# Wait for X server in background, then start services
(
# Log current environment
echo "[init] $(date) - Starting background services" >> /config/logs/startup.log
echo "[init] Running as user: $(whoami)" >> /config/logs/startup.log
echo "[init] Python version: $(python3 --version 2>&1)" >> /config/logs/startup.log
# Wait for X server to be ready
echo "[init] Waiting for X server..." >> /config/logs/startup.log
timeout=60
while [ $timeout -gt 0 ]; do
if DISPLAY=:0 xdpyinfo >/dev/null 2>&1; then
echo "[init] X server is ready!" >> /config/logs/startup.log
break
fi
sleep 1
((timeout--))
done
if [ $timeout -eq 0 ]; then
echo "[init] ERROR: X server failed to start within 60 seconds" >> /config/logs/startup.log
exit 1
fi
# Give X server and desktop environment time to fully initialize
echo "[init] Waiting 10 seconds for desktop environment to stabilize..." >> /config/logs/startup.log
sleep 10
# Start notification bridge with proper permissions
echo "[init] Starting notification bridge..." >> /config/logs/startup.log
s6-setuidgid abc env DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS:-unix:path=/run/user/911/bus}" \
/usr/local/bin/notification-bridge >> /config/logs/notification-bridge.log 2>&1 &
NOTIF_PID=$!
echo "[init] Notification bridge started with PID: $NOTIF_PID" >> /config/logs/startup.log
# Verify it's running
sleep 1
if kill -0 $NOTIF_PID 2>/dev/null; then
echo "[init] Notification bridge is running" >> /config/logs/startup.log
else
echo "[init] WARNING: Notification bridge may have failed to start" >> /config/logs/startup.log
fi
# Start window title sync with proper permissions
echo "[init] Starting window title sync..." >> /config/logs/startup.log
s6-setuidgid abc env DISPLAY=:0 /usr/local/bin/window-title-sync >> /config/logs/window-title-sync.log 2>&1 &
TITLE_PID=$!
echo "[init] Window title sync started with PID: $TITLE_PID" >> /config/logs/startup.log
# Verify it's running
sleep 1
if kill -0 $TITLE_PID 2>/dev/null; then
echo "[init] Window title sync is running" >> /config/logs/startup.log
else
echo "[init] WARNING: Window title sync may have failed to start" >> /config/logs/startup.log
fi
echo "[init] All background services started successfully at $(date)" >> /config/logs/startup.log
# Keep log files readable
chown abc:abc /config/logs/*.log 2>/dev/null
) &
echo "[init] Background services initialization launched"

View File

@@ -19,6 +19,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
wget \
libnotify-bin \
notification-daemon \
dbus-x11 \
python3 \
python3-gi \
python3-xlib \
gir1.2-notify-0.7 \
xdotool \
wmctrl \
x11-utils \
# GPU/OpenGL libraries for RuneLite GPU plugin
mesa-utils \
libgl1-mesa-dri \
@@ -33,13 +41,24 @@ RUN if [ -f /usr/share/selkies/www/index.html ]; then \
sed -i '/<\/head>/r /usr/share/selkies/www/webapp-meta.html' /usr/share/selkies/www/index.html ; \
fi
# Enable Selkies notification forwarding to browser
ENV SELKIES_ENABLE_NOTIFY="true"
ENV RUNELITE_URL="https://github.com/runelite/launcher/releases/latest/download/RuneLite.jar"
ADD runelite /usr/local/bin
COPY notification-bridge.py /usr/local/bin/notification-bridge
COPY window-title-sync.py /usr/local/bin/window-title-sync
COPY test-features.sh /usr/local/bin/test-features
COPY 90-start-services /etc/cont-init.d/90-start-services
RUN wget $RUNELITE_URL -P /usr/local/bin \
&& chmod +x /usr/local/bin/RuneLite.jar \
&& chmod +x /usr/local/bin/runelite
&& chmod +x /usr/local/bin/runelite \
&& chmod +x /usr/local/bin/notification-bridge \
&& chmod +x /usr/local/bin/window-title-sync \
&& chmod +x /usr/local/bin/test-features \
&& chmod +x /etc/cont-init.d/90-start-services
# Configure window manager to hide title bars and maximize windows
RUN mkdir -p /etc/xdg/openbox

43
notification-bridge.py Normal file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""
D-Bus notification bridge for Selkies
Monitors desktop notifications and forwards them to the browser via Web Notifications API
"""
import gi # type: ignore
gi.require_version('Notify', '0.7') # type: ignore
from gi.repository import Notify, GLib # type: ignore
import sys
import os
def on_notification(notification):
"""Callback when a notification is received"""
# Selkies will automatically capture notifications sent via libnotify
# This script ensures the notification daemon is properly initialized
pass
def main():
# Ensure DISPLAY is set
if not os.getenv('DISPLAY'):
os.environ['DISPLAY'] = ':0'
print("Set DISPLAY to :0", file=sys.stderr, flush=True)
# Initialize libnotify
if not Notify.init("notification-bridge"): # type: ignore
print("Failed to initialize libnotify", file=sys.stderr, flush=True)
sys.exit(1)
print("Notification bridge started - forwarding desktop notifications to browser", flush=True)
print(f"D-Bus session address: {os.getenv('DBUS_SESSION_BUS_ADDRESS', 'not set')}", flush=True)
# Keep the script running to maintain D-Bus connection
try:
loop = GLib.MainLoop() # type: ignore
print("Entering main loop - ready to forward notifications", flush=True)
loop.run()
except KeyboardInterrupt:
print("\nNotification bridge stopped", flush=True)
Notify.uninit() # type: ignore
if __name__ == "__main__":
main()

38
start-services.sh Normal file
View File

@@ -0,0 +1,38 @@
#!/bin/bash
# Startup script for notification bridge and window title sync
# Waits for X11 to be ready and starts background services with logging
LOG_DIR="/config/logs"
mkdir -p "$LOG_DIR"
# Wait for X server to be ready
echo "Waiting for X server..." >> "$LOG_DIR/startup.log"
timeout=30
while [ $timeout -gt 0 ]; do
if xdpyinfo >/dev/null 2>&1; then
echo "X server is ready!" >> "$LOG_DIR/startup.log"
break
fi
sleep 1
((timeout--))
done
if [ $timeout -eq 0 ]; then
echo "ERROR: X server failed to start within 30 seconds" >> "$LOG_DIR/startup.log"
exit 1
fi
# Give X server a moment to fully initialize
sleep 2
# Start notification bridge
echo "Starting notification bridge..." >> "$LOG_DIR/startup.log"
/usr/local/bin/notification-bridge >> "$LOG_DIR/notification-bridge.log" 2>&1 &
echo "Notification bridge PID: $!" >> "$LOG_DIR/startup.log"
# Start window title sync
echo "Starting window title sync..." >> "$LOG_DIR/startup.log"
/usr/local/bin/window-title-sync >> "$LOG_DIR/window-title-sync.log" 2>&1 &
echo "Window title sync PID: $!" >> "$LOG_DIR/startup.log"
echo "All background services started successfully" >> "$LOG_DIR/startup.log"

48
test-features.sh Normal file
View File

@@ -0,0 +1,48 @@
#!/bin/bash
# Test script to verify notifications and title changes are working
# Run this inside the container to test the features
echo "Testing notification and title sync features..."
# Test notification
echo "Sending test notification..."
notify-send "Test Notification" "If you see this in your browser, notifications are working!" -u normal
# Test window title detection
echo ""
echo "Checking for RuneLite window..."
if xdotool search --name "RuneLite" > /dev/null 2>&1; then
WINDOW_ID=$(xdotool search --name "RuneLite" | head -1)
WINDOW_TITLE=$(xdotool getwindowname "$WINDOW_ID")
echo "Found RuneLite window: $WINDOW_TITLE"
else
echo "RuneLite window not found. Make sure RuneLite is running."
fi
# Check if services are running
echo ""
echo "Checking service status..."
if pgrep -f "notification-bridge" > /dev/null; then
echo "✓ Notification bridge is running (PID: $(pgrep -f 'notification-bridge'))"
else
echo "✗ Notification bridge is NOT running"
fi
if pgrep -f "window-title-sync" > /dev/null; then
echo "✓ Window title sync is running (PID: $(pgrep -f 'window-title-sync'))"
else
echo "✗ Window title sync is NOT running"
fi
# Show log files if they exist
echo ""
echo "Log files location: /config/logs/"
if [ -d "/config/logs" ]; then
echo "Available logs:"
ls -lh /config/logs/
echo ""
echo "To view logs, run:"
echo " cat /config/logs/startup.log"
echo " cat /config/logs/notification-bridge.log"
echo " cat /config/logs/window-title-sync.log"
fi

118
window-title-sync.py Normal file
View File

@@ -0,0 +1,118 @@
#!/usr/bin/env python3
"""
Window title synchronization for Selkies
Monitors the active window title and updates the browser tab title dynamically
Note: This script is designed to run inside a Linux container with X11 tools installed.
Pylance warnings about missing imports are expected in the development environment.
"""
import subprocess
import time
import os
import sys
def get_active_window_title():
"""Get the title of the currently active window"""
try:
# Try using xdotool first
result = subprocess.run(
['xdotool', 'getactivewindow', 'getwindowname'],
capture_output=True,
text=True,
timeout=1
)
if result.returncode == 0:
return result.stdout.strip()
except (subprocess.TimeoutExpired, FileNotFoundError):
pass
try:
# Fallback to wmctrl
result = subprocess.run(
['wmctrl', '-l'],
capture_output=True,
text=True,
timeout=1
)
if result.returncode == 0:
lines = result.stdout.strip().split('\n')
for line in lines:
if 'RuneLite' in line:
# Extract title (after the third column)
parts = line.split(None, 3)
if len(parts) >= 4:
return parts[3]
except (subprocess.TimeoutExpired, FileNotFoundError):
pass
return None
def update_browser_title(title):
"""Update the Selkies browser tab title"""
# Try multiple possible locations for Selkies title file
title_locations = [
'/opt/gst-web/title',
'/tmp/selkies_title',
'/var/run/selkies/title'
]
success = False
for title_file in title_locations:
try:
# Create directory if it doesn't exist
os.makedirs(os.path.dirname(title_file), exist_ok=True)
with open(title_file, 'w') as f:
f.write(title)
success = True
break
except Exception:
continue
if not success:
# Fallback: try to update via xdotool if title file doesn't work
try:
subprocess.run(['xdotool', 'set_desktop_name', title], timeout=1, check=False)
except Exception:
pass
def main():
print("Window title sync started - monitoring RuneLite window", flush=True)
# Ensure DISPLAY is set
if 'DISPLAY' not in os.environ:
os.environ['DISPLAY'] = ':0'
print("Set DISPLAY to :0", flush=True)
last_title = None
# Initial delay to let X11 and RuneLite window appear
print("Waiting for RuneLite window to appear...", flush=True)
time.sleep(10)
while True:
try:
current_title = get_active_window_title()
# Only update if title has changed and contains "RuneLite"
if current_title and 'RuneLite' in current_title and current_title != last_title:
print(f"Window title changed to: {current_title}", flush=True)
update_browser_title(current_title)
last_title = current_title
elif current_title and last_title is None:
# Log first title found for debugging
print(f"First window title detected: {current_title}", flush=True)
# Check every 2 seconds
time.sleep(2)
except KeyboardInterrupt:
print("\nWindow title sync stopped", flush=True)
break
except Exception as e:
print(f"Error in title monitoring: {e}", file=sys.stderr, flush=True)
time.sleep(5)
if __name__ == "__main__":
main()