#!/bin/bash echo "=== Beeper Bridge Remote Fix Tool ===" echo "Run this script ON THE REMOTE MACHINE where Docker is running." # Create the python repair script cat << 'PYTHON_SCRIPT' > fix_configs.py import sys import os import shutil # Install ruamel.yaml if missing try: from ruamel.yaml import YAML, YAMLError except ImportError: print("Installing ruamel.yaml...") import subprocess subprocess.check_call([sys.executable, "-m", "pip", "install", "ruamel.yaml"]) from ruamel.yaml import YAML, YAMLError yaml = YAML() yaml.preserve_quotes = True services = ["whatsapp", "telegram", "signal", "googlechat"] base_dir = "data" # Docker-friendly logging config (Stdout/Stderr) logging_config = { "version": 1, "formatters": { "colored": { "format": "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s", "datefmt": "%b %d %H:%M:%S" } }, "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "colored", "stream": "ext://sys.stdout" } }, "root": { "level": "INFO", "handlers": ["console"] } } def fix_config(service): config_path = os.path.join(base_dir, service, "config.yaml") if not os.path.exists(config_path): print(f"[{service}] Config not found at {config_path}. Skipping.") return print(f"[{service}] Checking config...") try: with open(config_path, 'r') as f: data = yaml.load(f) # If we reached here, YAML is valid. Apply patches. modified = False # 1. Fix Database URI if 'appservice' in data and 'database' in data['appservice']: db_conf = data['appservice']['database'] if isinstance(db_conf, str) and db_conf != "sqlite:////data/bridge.db": data['appservice']['database'] = "sqlite:////data/bridge.db" modified = True elif isinstance(db_conf, dict) and db_conf.get('uri') != "sqlite:////data/bridge.db": data['appservice']['database']['uri'] = "sqlite:////data/bridge.db" modified = True if 'database' in data and isinstance(data['database'], dict): if data['database'].get('uri') != "sqlite:////data/bridge.db": data['database']['uri'] = "sqlite:////data/bridge.db" modified = True # 2. Fix Logging (Always apply to ensure stdout is used) # We check if it looks different to avoid unnecessary writes, # but simplistic check is: is it using stream handler? current_logging = data.get('logging', {}) # Just overwrite it to be safe and ensure Docker compatibility data['logging'] = logging_config modified = True if modified: print(f"[{service}] Applying fixes (Logging/Database)...") with open(config_path, 'w') as f: yaml.dump(data, f) else: print(f"[{service}] Config looked OK.") except (YAMLError, Exception) as e: print(f"[{service}] ERROR: Config file is corrupt or invalid YAML.") print(f" -> {e}") backup_path = config_path + ".corrupt.bak" print(f"[{service}] ACTION: Moving corrupt config to {backup_path}") print(f"[{service}] ACTION: The container will regenerate a fresh valid config on next start.") shutil.move(config_path, backup_path) # Main execution if __name__ == "__main__": for service in services: fix_config(service) PYTHON_SCRIPT echo "[*] Executing fix script using python:3.11-slim container..." # We use a docker container to run the python script so we don't mess with host python docker run --rm \ -v "$(pwd):/work" \ -w /work \ python:3.11-slim \ sh -c "pip install ruamel.yaml && python fix_configs.py" # Cleanup rm fix_configs.py echo "=== Fix Complete ===" echo "1. If Google Chat config was corrupt, it was backed up." echo "2. Run 'docker compose restart' (or 'up -d') now." echo "3. The bridges should generate fresh configs or use the fixed ones."