- Fix sendThreadMessage room mismatch: use channelId from options instead of hardcoded controlRoomId - Add .catch() to fire-and-forget handleRoomMessage to prevent silent error swallowing - Wrap dispatchJob in try-catch for user-visible error reporting in handleFixCommand - Add MATRIX_BOT_USER_ID validation in connect() to prevent infinite message loops - Fix streamResponse error masking: wrap finally/catch side-effects in try-catch - Replace unsafe type assertion with public getClient() in MatrixRoomService - Add orphaned room warning in provisionRoom on DB failure - Add provider identity to Herald error logs - Add channelId to ThreadMessageOptions interface and all callers - Add missing env var warnings in BridgeModule factory - Fix JSON injection in setup-bot.sh: use jq for safe JSON construction Fixes #377 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
204 lines
7.4 KiB
Bash
Executable File
204 lines
7.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ==============================================
|
|
# Matrix Bot Account Setup Script
|
|
# ==============================================
|
|
#
|
|
# Creates the Mosaic bot user on the local Synapse instance and retrieves
|
|
# an access token. Idempotent — safe to run multiple times.
|
|
#
|
|
# Usage:
|
|
# docker/matrix/scripts/setup-bot.sh
|
|
# docker/matrix/scripts/setup-bot.sh --username custom-bot --password custom-pass
|
|
#
|
|
# Prerequisites:
|
|
# - Synapse must be running (docker compose -f ... up synapse)
|
|
# - Synapse must be healthy (check with: curl http://localhost:8008/health)
|
|
#
|
|
# Output:
|
|
# Prints the environment variables needed for MatrixService configuration.
|
|
#
|
|
# ==============================================
|
|
|
|
set -euo pipefail
|
|
|
|
# Defaults
|
|
SYNAPSE_URL="${SYNAPSE_URL:-http://localhost:8008}"
|
|
BOT_USERNAME="${BOT_USERNAME:-mosaic-bot}"
|
|
BOT_PASSWORD="${BOT_PASSWORD:-mosaic-bot-dev-password}"
|
|
BOT_DISPLAY_NAME="${BOT_DISPLAY_NAME:-Mosaic Bot}"
|
|
ADMIN_USERNAME="${ADMIN_USERNAME:-admin}"
|
|
ADMIN_PASSWORD="${ADMIN_PASSWORD:-admin-dev-password}"
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--username) BOT_USERNAME="$2"; shift 2 ;;
|
|
--password) BOT_PASSWORD="$2"; shift 2 ;;
|
|
--synapse-url) SYNAPSE_URL="$2"; shift 2 ;;
|
|
--admin-username) ADMIN_USERNAME="$2"; shift 2 ;;
|
|
--admin-password) ADMIN_PASSWORD="$2"; shift 2 ;;
|
|
--help|-h)
|
|
echo "Usage: $0 [OPTIONS]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --username NAME Bot username (default: mosaic-bot)"
|
|
echo " --password PASS Bot password (default: mosaic-bot-dev-password)"
|
|
echo " --synapse-url URL Synapse URL (default: http://localhost:8008)"
|
|
echo " --admin-username NAME Admin username (default: admin)"
|
|
echo " --admin-password PASS Admin password (default: admin-dev-password)"
|
|
echo " --help, -h Show this help"
|
|
exit 0
|
|
;;
|
|
*) echo "Unknown option: $1"; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
echo "=== Mosaic Stack — Matrix Bot Setup ==="
|
|
echo ""
|
|
echo "Synapse URL: ${SYNAPSE_URL}"
|
|
echo "Bot username: ${BOT_USERNAME}"
|
|
echo ""
|
|
|
|
# Wait for Synapse to be ready
|
|
echo "Checking Synapse health..."
|
|
for i in $(seq 1 30); do
|
|
if curl -fsSo /dev/null "${SYNAPSE_URL}/health" 2>/dev/null; then
|
|
echo "Synapse is healthy."
|
|
break
|
|
fi
|
|
if [ "$i" -eq 30 ]; then
|
|
echo "ERROR: Synapse is not responding at ${SYNAPSE_URL}/health after 30 attempts."
|
|
echo "Make sure Synapse is running:"
|
|
echo " docker compose -f docker/docker-compose.yml -f docker/docker-compose.matrix.yml up -d"
|
|
exit 1
|
|
fi
|
|
echo " Waiting for Synapse... (attempt ${i}/30)"
|
|
sleep 2
|
|
done
|
|
|
|
echo ""
|
|
|
|
# Step 1: Register admin account (if not exists)
|
|
echo "Step 1: Registering admin account '${ADMIN_USERNAME}'..."
|
|
ADMIN_REGISTER_RESPONSE=$(curl -sS -X POST "${SYNAPSE_URL}/_synapse/admin/v1/register" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{}" 2>/dev/null || true)
|
|
|
|
NONCE=$(echo "${ADMIN_REGISTER_RESPONSE}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('nonce',''))" 2>/dev/null || true)
|
|
|
|
if [ -n "${NONCE}" ]; then
|
|
# Generate HMAC for admin registration using the nonce
|
|
# For dev, we use register_new_matrix_user via docker exec instead
|
|
echo " Using docker exec to register admin via Synapse CLI..."
|
|
docker exec mosaic-synapse register_new_matrix_user \
|
|
-u "${ADMIN_USERNAME}" \
|
|
-p "${ADMIN_PASSWORD}" \
|
|
-a \
|
|
-c /data/homeserver.yaml \
|
|
http://localhost:8008 2>/dev/null && echo " Admin account created." || echo " Admin account already exists (or registration failed — continuing)."
|
|
else
|
|
echo " Attempting registration via docker exec..."
|
|
docker exec mosaic-synapse register_new_matrix_user \
|
|
-u "${ADMIN_USERNAME}" \
|
|
-p "${ADMIN_PASSWORD}" \
|
|
-a \
|
|
-c /data/homeserver.yaml \
|
|
http://localhost:8008 2>/dev/null && echo " Admin account created." || echo " Admin account already exists (or registration failed — continuing)."
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Step 2: Get admin access token
|
|
echo "Step 2: Obtaining admin access token..."
|
|
ADMIN_LOGIN_RESPONSE=$(curl -sS -X POST "${SYNAPSE_URL}/_matrix/client/v3/login" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$(jq -n \
|
|
--arg user "$ADMIN_USERNAME" \
|
|
--arg pw "$ADMIN_PASSWORD" \
|
|
'{type: "m.login.password", identifier: {type: "m.id.user", user: $user}, password: $pw}')" \
|
|
2>/dev/null)
|
|
|
|
ADMIN_TOKEN=$(echo "${ADMIN_LOGIN_RESPONSE}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || true)
|
|
|
|
if [ -z "${ADMIN_TOKEN}" ]; then
|
|
echo "ERROR: Could not obtain admin access token."
|
|
echo "Response: ${ADMIN_LOGIN_RESPONSE}"
|
|
echo ""
|
|
echo "Try registering the admin account manually:"
|
|
echo " docker exec -it mosaic-synapse register_new_matrix_user -u ${ADMIN_USERNAME} -p ${ADMIN_PASSWORD} -a -c /data/homeserver.yaml http://localhost:8008"
|
|
exit 1
|
|
fi
|
|
echo " Admin token obtained."
|
|
|
|
echo ""
|
|
|
|
# Step 3: Register bot account via admin API (idempotent)
|
|
echo "Step 3: Registering bot account '${BOT_USERNAME}'..."
|
|
BOT_REGISTER_RESPONSE=$(curl -sS -X PUT "${SYNAPSE_URL}/_synapse/admin/v2/users/@${BOT_USERNAME}:localhost" \
|
|
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$(jq -n \
|
|
--arg pw "$BOT_PASSWORD" \
|
|
--arg dn "$BOT_DISPLAY_NAME" \
|
|
'{password: $pw, displayname: $dn, admin: false, deactivated: false}')" \
|
|
2>/dev/null)
|
|
|
|
BOT_EXISTS=$(echo "${BOT_REGISTER_RESPONSE}" | python3 -c "import sys,json; d=json.load(sys.stdin); print('yes' if d.get('name') else 'no')" 2>/dev/null || echo "no")
|
|
|
|
if [ "${BOT_EXISTS}" = "yes" ]; then
|
|
echo " Bot account '@${BOT_USERNAME}:localhost' is ready."
|
|
else
|
|
echo " WARNING: Bot registration response unexpected: ${BOT_REGISTER_RESPONSE}"
|
|
echo " Continuing anyway — bot may already exist."
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Step 4: Get bot access token
|
|
echo "Step 4: Obtaining bot access token..."
|
|
BOT_LOGIN_RESPONSE=$(curl -sS -X POST "${SYNAPSE_URL}/_matrix/client/v3/login" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$(jq -n \
|
|
--arg user "$BOT_USERNAME" \
|
|
--arg pw "$BOT_PASSWORD" \
|
|
'{type: "m.login.password", identifier: {type: "m.id.user", user: $user}, password: $pw}')" \
|
|
2>/dev/null)
|
|
|
|
BOT_TOKEN=$(echo "${BOT_LOGIN_RESPONSE}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || true)
|
|
|
|
if [ -z "${BOT_TOKEN}" ]; then
|
|
echo "ERROR: Could not obtain bot access token."
|
|
echo "Response: ${BOT_LOGIN_RESPONSE}"
|
|
exit 1
|
|
fi
|
|
|
|
echo " Bot token obtained."
|
|
echo ""
|
|
|
|
# Step 5: Output configuration
|
|
echo "============================================"
|
|
echo " Matrix Bot Setup Complete"
|
|
echo "============================================"
|
|
echo ""
|
|
echo "Add the following to your .env file:"
|
|
echo ""
|
|
echo " # Matrix Bridge Configuration"
|
|
echo " MATRIX_HOMESERVER_URL=http://localhost:8008"
|
|
echo " MATRIX_ACCESS_TOKEN=${BOT_TOKEN}"
|
|
echo " MATRIX_BOT_USER_ID=@${BOT_USERNAME}:localhost"
|
|
echo " MATRIX_SERVER_NAME=localhost"
|
|
echo ""
|
|
echo "Or, if running the API inside Docker (same compose network):"
|
|
echo ""
|
|
echo " MATRIX_HOMESERVER_URL=http://synapse:8008"
|
|
echo " MATRIX_ACCESS_TOKEN=${BOT_TOKEN}"
|
|
echo " MATRIX_BOT_USER_ID=@${BOT_USERNAME}:localhost"
|
|
echo " MATRIX_SERVER_NAME=localhost"
|
|
echo ""
|
|
echo "Element Web is available at: http://localhost:8501"
|
|
echo " Login with any registered user to test messaging."
|
|
echo ""
|
|
echo "Admin account: ${ADMIN_USERNAME} / ${ADMIN_PASSWORD}"
|
|
echo "Bot account: ${BOT_USERNAME} / ${BOT_PASSWORD}"
|
|
echo "============================================"
|