#!/bin/bash set -eu echo "=> Setting up mautrix-whatsapp bridge" # Create necessary directories mkdir -p /app/data mkdir -p /app/data/logs # For Cloudron, we run as the cloudron user UID=$(id -u cloudron) GID=$(id -g cloudron) # Set ownership early to avoid permission issues chown -R cloudron:cloudron /app/data # Configuration file paths CONFIG_PATH="/app/data/config.yaml" REGISTRATION_PATH="/app/data/registration.yaml" BACKUP_PATH="/app/data/config.yaml.bak" # Try to generate config from built-in template if [ ! -f "$CONFIG_PATH" ]; then echo "=> Attempting to generate configuration using mautrix-whatsapp" # Run as cloudron user to avoid permission issues and change to data directory cd /app/data gosu cloudron:cloudron /app/pkg/mautrix-whatsapp -e -c "$CONFIG_PATH" # Configure basic settings first before generating registration if [ -f "$CONFIG_PATH" ]; then echo "=> Applying basic Cloudron configuration to generated config" # Configure for Cloudron environment if [ -n "${CLOUDRON_POSTGRESQL_URL:-}" ]; then echo "=> Configuring PostgreSQL database: $CLOUDRON_POSTGRESQL_URL" # Add SSL mode disable to Cloudron PostgreSQL URL if not already present if [[ "$CLOUDRON_POSTGRESQL_URL" == *"sslmode="* ]]; then DB_URL="$CLOUDRON_POSTGRESQL_URL" else DB_URL="$CLOUDRON_POSTGRESQL_URL?sslmode=disable" fi yq -i -y '.database.uri = "'"$DB_URL"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure database" fi if [ -n "${CLOUDRON_APP_DOMAIN:-}" ]; then echo "=> Configuring homeserver and appservice settings for domain: $CLOUDRON_APP_DOMAIN" # Extract base domain (e.g., whatsapp.matrix.due.ren -> due.ren) BASE_DOMAIN=$(echo "$CLOUDRON_APP_DOMAIN" | rev | cut -d. -f1-2 | rev) echo "=> Base domain extracted: $BASE_DOMAIN" # Update homeserver configuration yq -i -y '.homeserver.address = "https://matrix.'"$BASE_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure homeserver address" yq -i -y '.homeserver.domain = "'"$BASE_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure homeserver domain" # Update appservice configuration yq -i -y '.appservice.address = "https://'"$CLOUDRON_APP_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure appservice address" yq -i -y '.appservice.public_address = "https://'"$CLOUDRON_APP_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure appservice public_address" yq -i -y '.appservice.hostname = "0.0.0.0"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure appservice hostname" yq -i -y '.appservice.port = 29318' "$CONFIG_PATH" || echo "=> ERROR: Could not configure appservice port" # Configure permissions for base domain users yq -i -y '.bridge.permissions."'"$BASE_DOMAIN"'" = "user"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure permissions" # Configure cleanup on logout to delete everything yq -i -y '.bridge.cleanup_on_logout.enabled = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup enabled" yq -i -y '.bridge.cleanup_on_logout.manual.private = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup manual private" yq -i -y '.bridge.cleanup_on_logout.manual.relayed = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup manual relayed" yq -i -y '.bridge.cleanup_on_logout.manual.shared_no_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup manual shared_no_users" yq -i -y '.bridge.cleanup_on_logout.manual.shared_has_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup manual shared_has_users" yq -i -y '.bridge.cleanup_on_logout.bad_credentials.private = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup bad_credentials private" yq -i -y '.bridge.cleanup_on_logout.bad_credentials.relayed = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup bad_credentials relayed" yq -i -y '.bridge.cleanup_on_logout.bad_credentials.shared_no_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup bad_credentials shared_no_users" yq -i -y '.bridge.cleanup_on_logout.bad_credentials.shared_has_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup bad_credentials shared_has_users" # Configure browser name for better WhatsApp compatibility yq -i -y '.network.browser_name = "ANDROID_PHONE"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure browser_name" # Configure end-to-bridge encryption with best practices yq -i -y '.encryption.allow = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption allow" yq -i -y '.encryption.default = false' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption default" yq -i -y '.encryption.require = false' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption require" yq -i -y '.encryption.appservice = false' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption appservice" yq -i -y '.encryption.plaintext_mentions = false' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption plaintext_mentions" yq -i -y '.encryption.delete_keys.delete_outbound_on_ack = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption delete_outbound_on_ack" yq -i -y '.encryption.delete_keys.dont_store_outbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption dont_store_outbound" yq -i -y '.encryption.delete_keys.ratchet_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption ratchet_on_decrypt" yq -i -y '.encryption.delete_keys.delete_fully_used_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption delete_fully_used_on_decrypt" yq -i -y '.encryption.delete_keys.delete_prev_on_new_session = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption delete_prev_on_new_session" yq -i -y '.encryption.delete_keys.delete_on_device_delete = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption delete_on_device_delete" yq -i -y '.encryption.delete_keys.periodically_delete_expired = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption periodically_delete_expired" yq -i -y '.encryption.delete_keys.delete_outdated_inbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption delete_outdated_inbound" yq -i -y '.encryption.verification_levels.receive = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption verification receive" yq -i -y '.encryption.verification_levels.send = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption verification send" yq -i -y '.encryption.verification_levels.share = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption verification share" fi # Now generate registration with proper config echo "=> Generating registration file" gosu cloudron:cloudron /app/pkg/mautrix-whatsapp -g -c "$CONFIG_PATH" -r "$REGISTRATION_PATH" # Fix registration file regex patterns to use base domain instead of homeserver domain if [ -f "$REGISTRATION_PATH" ] && [ -n "${CLOUDRON_APP_DOMAIN:-}" ]; then echo "=> Fixing registration file regex patterns for domain: $BASE_DOMAIN" # Fix user regex patterns to use base domain instead of matrix subdomain yq -i -y '.namespaces.users[0].regex = "^@whatsappbot:'"$BASE_DOMAIN"'$"' "$REGISTRATION_PATH" || echo "=> ERROR: Could not fix whatsappbot regex" yq -i -y '.namespaces.users[1].regex = "^@whatsapp_.*:'"$BASE_DOMAIN"'$"' "$REGISTRATION_PATH" || echo "=> ERROR: Could not fix whatsapp_.* regex" fi chown cloudron:cloudron "$CONFIG_PATH" "$REGISTRATION_PATH" 2>/dev/null || true echo "=> Configuration applied successfully" fi if [ ! -f "$CONFIG_PATH" ]; then echo "=> ERROR: Config generation failed and no config file was created" echo "=> Will try to start without config to see error messages" fi else echo "=> Using existing configuration" # Fix configuration in existing config if needed if [ -f "$CONFIG_PATH" ]; then echo "=> Config file exists, applying fixes..." # Always fix configuration on every start to ensure proper settings echo "=> Applying configuration fixes" # Fix database configuration if needed if [ -n "${CLOUDRON_POSTGRESQL_URL:-}" ]; then # Add SSL mode disable to Cloudron PostgreSQL URL if not already present if [[ "$CLOUDRON_POSTGRESQL_URL" == *"sslmode="* ]]; then DB_URL="$CLOUDRON_POSTGRESQL_URL" else DB_URL="$CLOUDRON_POSTGRESQL_URL?sslmode=disable" fi CURRENT_DB_URI=$(yq -r '.database.uri' "$CONFIG_PATH" 2>/dev/null || echo "") if [ "$CURRENT_DB_URI" != "$DB_URL" ]; then echo "=> Updating database configuration" yq -i -y '.database.uri = "'"$DB_URL"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not update database" fi fi # Fix homeserver configuration if needed if [ -n "${CLOUDRON_APP_DOMAIN:-}" ]; then # Extract base domain (e.g., whatsapp.matrix.due.ren -> due.ren) BASE_DOMAIN=$(echo "$CLOUDRON_APP_DOMAIN" | rev | cut -d. -f1-2 | rev) CURRENT_DOMAIN=$(yq -r '.homeserver.domain' "$CONFIG_PATH" 2>/dev/null || echo "") if [ "$CURRENT_DOMAIN" != "$BASE_DOMAIN" ]; then echo "=> Updating homeserver configuration" echo "=> Setting homeserver.address to: https://matrix.$BASE_DOMAIN" echo "=> Setting homeserver.domain to: $BASE_DOMAIN" echo "=> Setting appservice.address to: https://$CLOUDRON_APP_DOMAIN" yq -i -y '.homeserver.address = "https://matrix.'"$BASE_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not update homeserver address" yq -i -y '.homeserver.domain = "'"$BASE_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not update homeserver domain" yq -i -y '.appservice.address = "https://'"$CLOUDRON_APP_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not update appservice address" yq -i -y '.appservice.public_address = "https://'"$CLOUDRON_APP_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not update appservice public_address" yq -i -y '.appservice.hostname = "0.0.0.0"' "$CONFIG_PATH" || echo "=> ERROR: Could not update appservice hostname" yq -i -y '.appservice.port = 29318' "$CONFIG_PATH" || echo "=> ERROR: Could not update appservice port" # Configure permissions for base domain users yq -i -y '.bridge.permissions."'"$BASE_DOMAIN"'" = "user"' "$CONFIG_PATH" || echo "=> ERROR: Could not update permissions" # Configure cleanup on logout to delete everything yq -i -y '.bridge.cleanup_on_logout.enabled = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup enabled" yq -i -y '.bridge.cleanup_on_logout.manual.private = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup manual private" yq -i -y '.bridge.cleanup_on_logout.manual.relayed = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup manual relayed" yq -i -y '.bridge.cleanup_on_logout.manual.shared_no_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup manual shared_no_users" yq -i -y '.bridge.cleanup_on_logout.manual.shared_has_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup manual shared_has_users" yq -i -y '.bridge.cleanup_on_logout.bad_credentials.private = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup bad_credentials private" yq -i -y '.bridge.cleanup_on_logout.bad_credentials.relayed = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup bad_credentials relayed" yq -i -y '.bridge.cleanup_on_logout.bad_credentials.shared_no_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup bad_credentials shared_no_users" yq -i -y '.bridge.cleanup_on_logout.bad_credentials.shared_has_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup bad_credentials shared_has_users" # Configure browser name for better WhatsApp compatibility yq -i -y '.network.browser_name = "ANDROID_PHONE"' "$CONFIG_PATH" || echo "=> ERROR: Could not update browser_name" # Configure end-to-bridge encryption with best practices yq -i -y '.encryption.allow = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption allow" yq -i -y '.encryption.default = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption default" yq -i -y '.encryption.require = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption require" yq -i -y '.encryption.appservice = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption appservice" yq -i -y '.encryption.plaintext_mentions = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption plaintext_mentions" yq -i -y '.encryption.delete_keys.delete_outbound_on_ack = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_outbound_on_ack" yq -i -y '.encryption.delete_keys.dont_store_outbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption dont_store_outbound" yq -i -y '.encryption.delete_keys.ratchet_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption ratchet_on_decrypt" yq -i -y '.encryption.delete_keys.delete_fully_used_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_fully_used_on_decrypt" yq -i -y '.encryption.delete_keys.delete_prev_on_new_session = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_prev_on_new_session" yq -i -y '.encryption.delete_keys.delete_on_device_delete = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_on_device_delete" yq -i -y '.encryption.delete_keys.periodically_delete_expired = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption periodically_delete_expired" yq -i -y '.encryption.delete_keys.delete_outdated_inbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_outdated_inbound" yq -i -y '.encryption.verification_levels.receive = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification receive" yq -i -y '.encryption.verification_levels.send = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification send" yq -i -y '.encryption.verification_levels.share = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification share" # Verify the changes were applied echo "=> Verifying configuration changes:" echo "=> Current homeserver.address: $(yq -r '.homeserver.address' "$CONFIG_PATH")" echo "=> Current homeserver.domain: $(yq -r '.homeserver.domain' "$CONFIG_PATH")" fi fi # Fix logging configuration yq -i -y '.logging.writers[1].filename = "/app/data/mautrix-whatsapp.log"' "$CONFIG_PATH" 2>/dev/null || true # Always apply browser name and encryption settings (regardless of domain changes) echo "=> Applying browser name and encryption configuration" yq -i -y '.network.browser_name = "ANDROID_PHONE"' "$CONFIG_PATH" || echo "=> ERROR: Could not update browser_name" yq -i -y '.encryption.allow = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption allow" yq -i -y '.encryption.default = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption default" yq -i -y '.encryption.require = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption require" yq -i -y '.encryption.appservice = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption appservice" yq -i -y '.encryption.plaintext_mentions = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption plaintext_mentions" yq -i -y '.encryption.delete_keys.delete_outbound_on_ack = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_outbound_on_ack" yq -i -y '.encryption.delete_keys.dont_store_outbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption dont_store_outbound" yq -i -y '.encryption.delete_keys.ratchet_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption ratchet_on_decrypt" yq -i -y '.encryption.delete_keys.delete_fully_used_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_fully_used_on_decrypt" yq -i -y '.encryption.delete_keys.delete_prev_on_new_session = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_prev_on_new_session" yq -i -y '.encryption.delete_keys.delete_on_device_delete = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_on_device_delete" yq -i -y '.encryption.delete_keys.periodically_delete_expired = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption periodically_delete_expired" yq -i -y '.encryption.delete_keys.delete_outdated_inbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_outdated_inbound" yq -i -y '.encryption.verification_levels.receive = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification receive" yq -i -y '.encryption.verification_levels.send = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification send" yq -i -y '.encryption.verification_levels.share = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification share" # Update registration file with correct URL and fix regex patterns if [ -f "$REGISTRATION_PATH" ] && [ -n "${CLOUDRON_APP_DOMAIN:-}" ]; then yq -i -y '.url = "https://'"$CLOUDRON_APP_DOMAIN"'"' "$REGISTRATION_PATH" 2>/dev/null || true # Fix user regex patterns to use base domain instead of matrix subdomain yq -i -y '.namespaces.users[0].regex = "^@whatsappbot:'"$BASE_DOMAIN"'$"' "$REGISTRATION_PATH" || echo "=> ERROR: Could not fix whatsappbot regex" yq -i -y '.namespaces.users[1].regex = "^@whatsapp_.*:'"$BASE_DOMAIN"'$"' "$REGISTRATION_PATH" || echo "=> ERROR: Could not fix whatsapp_.* regex" fi # Let the bridge handle token generation automatically - remove any placeholder tokens if [ -f "$REGISTRATION_PATH" ]; then echo "=> Registration file generated, letting bridge handle token management" fi fi echo "=> Configuration fixes completed" fi # Final permission fix before starting echo "=> Setting final permissions..." chown -R cloudron:cloudron /app/data echo "=> Permissions set" # Configure TLS if certificates are available if [ -f "/run/tls/tls.crt" ] && [ -f "/run/tls/tls.key" ]; then echo "=> Configuring TLS certificates" yq -i -y '.appservice.tls_cert = "/run/tls/tls.crt"' "$CONFIG_PATH" yq -i -y '.appservice.tls_key = "/run/tls/tls.key"' "$CONFIG_PATH" fi # Start the bridge from the data directory to ensure relative paths work echo "=> Starting mautrix-whatsapp bridge" echo "=> Config path: $CONFIG_PATH" echo "=> Registration path: $REGISTRATION_PATH" echo "=> Working directory: $(pwd)" cd /app/data echo "=> About to exec bridge binary..." exec gosu cloudron:cloudron /app/pkg/mautrix-whatsapp -c "$CONFIG_PATH"