# shellcheck shell=bash # ============================================================================ # Shared helpers for the scripts that drive a signal-cli-rest-api container. # # Sourced, never executed. Callers must define API before sourcing. # # Prompts read from $SIGNAL_TTY, not /dev/tty directly. The redirect is # needed because these functions' stdout is captured by the caller, but a # hard-coded /dev/tty makes the interactive path impossible to rehearse # without a real terminal -- so it is a seam, exactly like the injected # clock and transport on the Python side. # ============================================================================ : "${SIGNAL_TTY:=/dev/tty}" # Wait until a URL answers, or give up with a named error. signal_wait_for_url() { local url="$1" name="$2" attempts="${3:-150}" attempt=0 until curl -sf "$url" >/dev/null 2>&1; do attempt=$((attempt + 1)) if [[ $attempt -ge $attempts ]]; then echo "Error: $name did not come up at $url" >&2 return 1 fi sleep 0.4 done } # Refuse to continue unless the container exposes a receive WebSocket. signal_verify_mode() { local mode mode="$(curl -sf "${API}/v1/about" | jq -r '.mode // "unknown"')" if [[ "$mode" != *json-rpc* ]]; then echo "Error: container mode is '$mode'." >&2 echo " /v1/receive is only a WebSocket in a json-rpc mode." >&2 return 1 fi echo "== container mode: $mode" } # Print the group id the user picks. Every prompt goes to stderr because this # function's stdout is captured by the caller. signal_choose_group() { local number="$1" groups choice id # Fetched ONCE: two calls could return a different order, so the index the # user picks would resolve against a different list than the one shown. groups="$(curl -sf "${API}/v1/groups/${number}")" if [[ -z "$groups" || "$groups" == "[]" ]]; then echo "Error: no groups on ${number}." >&2 echo " Add the bot to a private group from your phone first," >&2 echo " then re-run this script." >&2 return 1 fi echo "== groups on this account:" >&2 printf '%s' "$groups" | jq -r '.[] | "\(.id)\t\(.name // "(unnamed)")"' \ | nl -w3 -s' ' >&2 echo >&2 read -r -p "Pick the number of the PRIVATE group to use: " choice <"$SIGNAL_TTY" >&2 if ! [[ "$choice" =~ ^[0-9]+$ ]]; then echo "Error: '$choice' is not a number" >&2 return 1 fi id="$(printf '%s' "$groups" | jq -r --argjson n "$choice" '.[$n - 1].id // empty')" if [[ -z "$id" ]]; then echo "Error: no group at position $choice" >&2 return 1 fi printf '%s' "$id" } # Write .env for the bot. GROUP_ID == GROUP_ID_SEND so replies land in the # same group that triggered them. signal_write_env() { local repo_root="$1" number="$2" group="$3" cat > "${repo_root}/.env" <