diff --git a/script/cnc.sh b/script/cnc.sh new file mode 100755 index 0000000..37df991 --- /dev/null +++ b/script/cnc.sh @@ -0,0 +1,465 @@ +#!/bin/bash + +echo "ЕСЛИ У ВАС ЕСТЬ ПРОБЛЕМЫ - Я В КУРСЕ, ПРОЕКТ В БЕТЕ, ПО ПРОБЛЕМАМ В ЧАТ t.me/openlibrecommunity ИЛИ ВООБЩЕ НЕКУДА, ЖДИТЕ РЕЛИЗА" + + +set -e + +PODMAN_ID=$(tr -dc 'a-z0-9' /dev/null; then + echo "[!] Installing Podman..." + + if [ "$(id -u)" -eq 0 ]; then + SUDO="" + elif command -v sudo &> /dev/null; then + SUDO="sudo" + elif command -v doas &> /dev/null; then + SUDO="doas" + else + echo "[X] No sudo/doas found and not running as root. Cannot install podman." + exit 1 + fi + + if command -v apt &> /dev/null; then + echo "[*] Detected apt (Debian/Ubuntu)" + $SUDO apt update + $SUDO apt install -y podman + elif command -v dnf &> /dev/null; then + echo "[*] Detected dnf (Fedora/RHEL)" + $SUDO dnf install -y podman + elif command -v yum &> /dev/null; then + echo "[*] Detected yum (CentOS/RHEL)" + $SUDO yum install -y podman + elif command -v pacman &> /dev/null; then + echo "[*] Detected pacman (Arch)" + $SUDO pacman -Sy --noconfirm podman + else + echo "[X] Unsupported package manager. Install podman manually." + exit 1 + fi +fi + +echo "[+] Using Podman" +echo "" + +validate_key() { + case "$1" in + *[!0-9a-fA-F]*) + return 1 + ;; + esac + [ "${#1}" -eq 64 ] +} + +echo "Select auth provider:" +echo " 1) jitsi" +echo " 2) telemost" +echo " 3) wbstream" +read -p "Enter choice [1-3, default: 1]: " AUTH_CHOICE + +case "$AUTH_CHOICE" in + 2) + AUTH="telemost" + ;; + 3) + AUTH="wbstream" + ;; + *) + AUTH="jitsi" + ;; +esac + +echo "[*] Using auth: $AUTH" +echo "" + +echo "Select transport:" +echo " 1) datachannel" +echo " 2) videochannel" +echo " 3) seichannel" +echo " 4) vp8channel" +read -p "Enter choice [1-4, default: 1]: " TRANSPORT_CHOICE + +case "$TRANSPORT_CHOICE" in + 2) + TRANSPORT="videochannel" + ;; + 3) + TRANSPORT="seichannel" + ;; + 4) + TRANSPORT="vp8channel" + ;; + *) + TRANSPORT="datachannel" + ;; +esac + +echo "[*] Using transport: $TRANSPORT" +echo "" + +if [ "$AUTH" = "jitsi" ]; then + echo "" + echo "Выберите Jitsi-сервер (проверьте в браузере, какой работает в вашей сети):" + echo " 1) https://meet.small-dm.ru/" + echo " 2) https://meet1.arbitr.ru/" + echo " 3) https://meet.handyweb.org/" + echo " 4) Другой (ввести вручную)" + read -p "Введите номер [1-4, по умолчанию: 1]: " JITSI_SERVER_CHOICE + + case "$JITSI_SERVER_CHOICE" in + 2) + JITSI_BASE_URL="https://meet1.arbitr.ru" + ;; + 3) + JITSI_BASE_URL="https://meet.handyweb.org" + ;; + 4) + read -p "Введите URL Jitsi-сервера: " JITSI_BASE_INPUT + JITSI_BASE_URL="${JITSI_BASE_INPUT%/}" + if [ -z "$JITSI_BASE_URL" ]; then + echo "[X] URL не может быть пустым" + exit 1 + fi + ;; + *) + JITSI_BASE_URL="https://meet.small-dm.ru" + ;; + esac + + read -p "Enter Jitsi room name or URL: " JITSI_ROOM_INPUT + if [ -z "$JITSI_ROOM_INPUT" ]; then + echo "[X] Jitsi room name/URL cannot be empty" + exit 1 + fi + + case "$JITSI_ROOM_INPUT" in + http://*|https://*|*/*) + ROOM_ID="$JITSI_ROOM_INPUT" + ;; + *) + ROOM_ID="$JITSI_BASE_URL/$JITSI_ROOM_INPUT" + ;; + esac +else + read -p "Enter Room ID: " ROOM_ID +fi + +if [ -z "$ROOM_ID" ]; then + echo "[X] Room ID/URL cannot be empty" + exit 1 +fi + +echo "" +read -p "Enter Encryption Key (hex): " KEY + +if [ -z "$KEY" ]; then + echo "[X] Encryption key cannot be empty" + exit 1 +fi + +if ! validate_key "$KEY"; then + echo "[X] Encryption key must be 64 hex characters" + exit 1 +fi + +echo "" +read -p "DNS server [default: 8.8.8.8:53]: " DNS_INPUT +DNS=${DNS_INPUT:-8.8.8.8:53} + +echo "" +read -p "SOCKS5 ip [default: 127.0.0.1]: " IP_INPUT +SOCKS_IP=${IP_INPUT:-127.0.0.1} + +echo "" +read -p "SOCKS5 port [default: 8808]: " PORT_INPUT +SOCKS_PORT=${PORT_INPUT:-8808} + +echo "" +read -p "SOCKS5 username (leave empty to disable auth): " SOCKS_USER_INPUT +SOCKS_USER=${SOCKS_USER_INPUT:-} + +SOCKS_PASS="" +if [ -n "$SOCKS_USER" ]; then + read -s -p "SOCKS5 password: " SOCKS_PASS_INPUT + echo "" + SOCKS_PASS=${SOCKS_PASS_INPUT:-} +fi + +case "$SOCKS_IP" in + 127.*|localhost|::1|\[::1\]) + ;; + *) + if [ -z "$SOCKS_USER" ] || [ -z "$SOCKS_PASS" ]; then + echo "[X] SOCKS auth required when binding outside loopback (set username and password)" + exit 1 + fi + ;; +esac + +# Transport-specific settings +VIDEO_W=1920; VIDEO_H=1080; VIDEO_FPS=30; VIDEO_BITRATE="2M"; VIDEO_HW="none" +VIDEO_CODEC="qrcode"; VIDEO_QR_SIZE=0; VIDEO_QR_RECOVERY="low" +VIDEO_TILE_MODULE=4; VIDEO_TILE_RS=20 +VP8_FPS=25; VP8_BATCH=1 +SEI_FPS=60; SEI_BATCH=64; SEI_FRAG=900; SEI_ACK=2000 + +if [ "$TRANSPORT" = "videochannel" ]; then + echo "" + echo "--- Videochannel settings ---" + + echo "" + echo "Video codec:" + echo " 1) qrcode" + echo " 2) tile (requires 1080x1080)" + read -p "Enter choice [1-2, default: 1]: " VCODEC_CHOICE + + case "$VCODEC_CHOICE" in + 2) + VIDEO_CODEC="tile" + VIDEO_W=1080 + VIDEO_H=1080 + echo "[*] Tile codec selected - forcing 1080x1080" + + read -p "Tile module size in pixels 1..270 [default: 4]: " VTILE_MOD_INPUT + VIDEO_TILE_MODULE=${VTILE_MOD_INPUT:-4} + + read -p "Tile Reed-Solomon parity percent 0..200 [default: 20]: " VTILE_RS_INPUT + VIDEO_TILE_RS=${VTILE_RS_INPUT:-20} + ;; + *) + VIDEO_CODEC="qrcode" + + read -p "Video width [default: 1920]: " VW_INPUT + VIDEO_W=${VW_INPUT:-1920} + + read -p "Video height [default: 1080]: " VH_INPUT + VIDEO_H=${VH_INPUT:-1080} + + read -p "QR error correction (low/medium/high/highest) [default: low]: " VQREC_INPUT + VIDEO_QR_RECOVERY=${VQREC_INPUT:-low} + + read -p "QR fragment size bytes [default: 0 (auto)]: " VQRSZ_INPUT + VIDEO_QR_SIZE=${VQRSZ_INPUT:-0} + ;; + esac + + read -p "Video FPS [default: 30]: " VFPS_INPUT + VIDEO_FPS=${VFPS_INPUT:-30} + + read -p "Video bitrate [default: 2M]: " VBRT_INPUT + VIDEO_BITRATE=${VBRT_INPUT:-2M} + + read -p "Hardware acceleration (none/nvenc) [default: none]: " VHW_INPUT + VIDEO_HW=${VHW_INPUT:-none} +fi + +if [ "$TRANSPORT" = "vp8channel" ]; then + echo "" + echo "--- VP8channel settings ---" + + read -p "VP8 FPS [default: 25]: " VP8FPS_INPUT + VP8_FPS=${VP8FPS_INPUT:-25} + + read -p "VP8 batch size (frames per tick) [default: 1]: " VP8BATCH_INPUT + VP8_BATCH=${VP8BATCH_INPUT:-1} +fi + +if [ "$TRANSPORT" = "seichannel" ]; then + echo "" + echo "--- SEIchannel settings ---" + + read -p "SEI FPS [default: 60]: " SEIFPS_INPUT + SEI_FPS=${SEIFPS_INPUT:-60} + + read -p "SEI batch size (frames per tick) [default: 64]: " SEIBATCH_INPUT + SEI_BATCH=${SEIBATCH_INPUT:-64} + + read -p "SEI fragment size in bytes [default: 900]: " SEIFRAG_INPUT + SEI_FRAG=${SEIFRAG_INPUT:-900} + + read -p "SEI ACK timeout in milliseconds [default: 2000]: " SEIACK_INPUT + SEI_ACK=${SEIACK_INPUT:-2000} +fi + +echo "" +echo "[*] Cleaning workspace..." +rm -rf "$WORK_DIR" +mkdir -p "$WORK_DIR" + +CACHE_DIR="${OLCRTC_CACHE_DIR:-$HOME/.cache/olcrtc}" +GOMOD_CACHE="$CACHE_DIR/gomod" +GO_BUILD_CACHE="$CACHE_DIR/gobuild" + +if [ "$NO_CACHE" = "1" ]; then + echo "[*] --no-cache: purging Go cache at $CACHE_DIR" + chmod -R u+w "$GOMOD_CACHE" "$GO_BUILD_CACHE" 2>/dev/null || true + if ! rm -rf "$GOMOD_CACHE" "$GO_BUILD_CACHE" 2>/dev/null; then + echo "[*] Falling back to in-container purge (files owned by container UID)..." + podman run --rm \ + -v "$CACHE_DIR":/cache:Z \ + "$IMAGE_NAME" \ + sh -c 'rm -rf /cache/gomod /cache/gobuild' + fi +fi + +mkdir -p "$GOMOD_CACHE" "$GO_BUILD_CACHE" +echo "[*] Using Go cache: $CACHE_DIR" + +echo "[*] Cloning repository..." +git clone --depth 1 --recurse-submodules --branch "$BRANCH" "$REPO_URL" "$WORK_DIR" + +echo "[*] Pulling Go image..." +podman pull "$IMAGE_NAME" + +echo "[*] Building OlcRTC..." +podman run --rm \ + --network host \ + -v "$WORK_DIR":/app:Z \ + -v "$GOMOD_CACHE":/go/pkg/mod:Z \ + -v "$GO_BUILD_CACHE":/root/.cache/go-build:Z \ + -w /app \ + "$IMAGE_NAME" \ + sh -c "go mod download && go build -trimpath -ldflags='-s -w' -o olcrtc ./cmd/olcrtc" + +if [ ! -f "$WORK_DIR/olcrtc" ]; then + echo "[X] Build failed" + exit 1 +fi + +# Generate YAML config +CONFIG_FILE="$WORK_DIR/client.yaml" +cat > "$CONFIG_FILE" <> "$CONFIG_FILE" <> "$CONFIG_FILE" <> "$CONFIG_FILE" <> "$CONFIG_FILE" <> "$CONFIG_FILE" < /dev/null; then + echo "[!] Installing Podman..." + + if [ "$(id -u)" -eq 0 ]; then + SUDO="" + elif command -v sudo &> /dev/null; then + SUDO="sudo" + elif command -v doas &> /dev/null; then + SUDO="doas" + else + echo "[X] No sudo/doas found and not running as root. Cannot install podman." + exit 1 + fi + + if command -v apt &> /dev/null; then + echo "[*] Detected apt (Debian/Ubuntu)" + $SUDO apt update + $SUDO apt install -y podman + elif command -v dnf &> /dev/null; then + echo "[*] Detected dnf (Fedora/RHEL)" + $SUDO dnf install -y podman + elif command -v yum &> /dev/null; then + echo "[*] Detected yum (CentOS/RHEL)" + $SUDO yum install -y podman + elif command -v pacman &> /dev/null; then + echo "[*] Detected pacman (Arch)" + $SUDO pacman -Sy --noconfirm podman + else + echo "[X] Unsupported package manager. Install podman manually." + exit 1 + fi +fi + +echo "[+] Using Podman" +echo "" + +validate_key() { + case "$1" in + *[!0-9a-fA-F]*) + return 1 + ;; + esac + [ "${#1}" -eq 64 ] +} + +echo "Select carrier:" +echo " 1) jitsi" +echo " 2) telemost" +echo " 3) wbstream" +read -p "Enter choice [1-3, default: 1]: " CARRIER_CHOICE + +case "$CARRIER_CHOICE" in + 2) + CARRIER="telemost" + ;; + 3) + CARRIER="wbstream" + ;; + *) + CARRIER="jitsi" + ;; +esac + +echo "[*] Using carrier: $CARRIER" +echo "" + +echo "Select transport:" +echo " 1) datachannel" +echo " 2) videochannel" +echo " 3) seichannel" +echo " 4) vp8channel" +read -p "Enter choice [1-4, default: 1]: " TRANSPORT_CHOICE + +case "$TRANSPORT_CHOICE" in + 2) + TRANSPORT="videochannel" + ;; + 3) + TRANSPORT="seichannel" + ;; + 4) + TRANSPORT="vp8channel" + ;; + *) + TRANSPORT="datachannel" + ;; +esac + +echo "[*] Using transport: $TRANSPORT" +echo "" + +GEN_ROOM=0 + +if [ "$CARRIER" = "jitsi" ]; then + echo "" + echo "Выберите Jitsi-сервер (проверьте в браузере, какой работает в вашей сети):" + echo " 1) https://meet.small-dm.ru/" + echo " 2) https://meet1.arbitr.ru/" + echo " 3) https://meet.handyweb.org/" + echo " 4) Другой (ввести вручную)" + read -p "Введите номер [1-4, по умолчанию: 1]: " JITSI_SERVER_CHOICE + + case "$JITSI_SERVER_CHOICE" in + 2) + JITSI_BASE_URL="https://meet1.arbitr.ru" + ;; + 3) + JITSI_BASE_URL="https://meet.handyweb.org" + ;; + 4) + read -p "Введите URL Jitsi-сервера: " JITSI_BASE_INPUT + JITSI_BASE_URL="${JITSI_BASE_INPUT%/}" + if [ -z "$JITSI_BASE_URL" ]; then + echo "[X] URL не может быть пустым" + exit 1 + fi + ;; + *) + JITSI_BASE_URL="https://meet.small-dm.ru" + ;; + esac + + echo "Room options:" + echo " 1) Auto-generate new room (recommended)" + echo " 2) Use specific room name or URL" + read -p "Enter choice [1-2, default: 1]: " ROOM_CHOICE + + case "$ROOM_CHOICE" in + 2) + read -p "Enter Jitsi room name or URL: " JITSI_ROOM_INPUT + if [ -z "$JITSI_ROOM_INPUT" ]; then + echo "[X] Jitsi room name/URL cannot be empty" + exit 1 + fi + + case "$JITSI_ROOM_INPUT" in + http://*|https://*|*/*) + ROOM_ID="$JITSI_ROOM_INPUT" + ;; + *) + ROOM_ID="$JITSI_BASE_URL/$JITSI_ROOM_INPUT" + ;; + esac + ;; + *) + JITSI_ROOM="olcrtc-$PODMAN_ID" + ROOM_ID="$JITSI_BASE_URL/$JITSI_ROOM" + echo "[*] Generated Jitsi room URL: $ROOM_ID" + ;; + esac +else + read -p "Enter Room ID: " ROOM_ID + if [ -z "$ROOM_ID" ]; then + echo "[X] Room ID/URL cannot be empty" + exit 1 + fi +fi + +echo "" +read -p "DNS server [default: 8.8.8.8:53]: " DNS_INPUT +DNS=${DNS_INPUT:-8.8.8.8:53} + +echo "" +read -p "Use SOCKS5 proxy for egress? (y/N): " USE_PROXY + +SOCKS_PROXY_ADDR="" +SOCKS_PROXY_PORT=0 + +if [[ "$USE_PROXY" =~ ^[Yy]$ ]]; then + read -p "Enter SOCKS5 proxy address [default: 127.0.0.1]: " PROXY_ADDR_INPUT + SOCKS_PROXY_ADDR=${PROXY_ADDR_INPUT:-127.0.0.1} + + read -p "Enter SOCKS5 proxy port [default: 1080]: " PROXY_PORT_INPUT + SOCKS_PROXY_PORT=${PROXY_PORT_INPUT:-1080} + + echo "[*] Will use SOCKS5 proxy: $SOCKS_PROXY_ADDR:$SOCKS_PROXY_PORT" +fi + +# Transport-specific settings +VIDEO_W=1920; VIDEO_H=1080; VIDEO_FPS=30; VIDEO_BITRATE="2M"; VIDEO_HW="none" +VIDEO_CODEC="qrcode"; VIDEO_QR_SIZE=0; VIDEO_QR_RECOVERY="low" +VIDEO_TILE_MODULE=4; VIDEO_TILE_RS=20 +VP8_FPS=25; VP8_BATCH=1 +SEI_FPS=60; SEI_BATCH=64; SEI_FRAG=900; SEI_ACK=2000 + +if [ "$TRANSPORT" = "videochannel" ]; then + echo "" + echo "--- Videochannel settings ---" + + echo "" + echo "Video codec:" + echo " 1) qrcode" + echo " 2) tile (requires 1080x1080)" + read -p "Enter choice [1-2, default: 1]: " VCODEC_CHOICE + + case "$VCODEC_CHOICE" in + 2) + VIDEO_CODEC="tile" + VIDEO_W=1080 + VIDEO_H=1080 + echo "[*] Tile codec selected - forcing 1080x1080" + + read -p "Tile module size in pixels 1..270 [default: 4]: " VTILE_MOD_INPUT + VIDEO_TILE_MODULE=${VTILE_MOD_INPUT:-4} + + read -p "Tile Reed-Solomon parity percent 0..200 [default: 20]: " VTILE_RS_INPUT + VIDEO_TILE_RS=${VTILE_RS_INPUT:-20} + ;; + *) + VIDEO_CODEC="qrcode" + + read -p "Video width [default: 1920]: " VW_INPUT + VIDEO_W=${VW_INPUT:-1920} + + read -p "Video height [default: 1080]: " VH_INPUT + VIDEO_H=${VH_INPUT:-1080} + + read -p "QR error correction (low/medium/high/highest) [default: low]: " VQREC_INPUT + VIDEO_QR_RECOVERY=${VQREC_INPUT:-low} + + read -p "QR fragment size bytes [default: 0 (auto)]: " VQRSZ_INPUT + VIDEO_QR_SIZE=${VQRSZ_INPUT:-0} + ;; + esac + + read -p "Video FPS [default: 30]: " VFPS_INPUT + VIDEO_FPS=${VFPS_INPUT:-30} + + read -p "Video bitrate [default: 2M]: " VBRT_INPUT + VIDEO_BITRATE=${VBRT_INPUT:-2M} + + read -p "Hardware acceleration (none/nvenc) [default: none]: " VHW_INPUT + VIDEO_HW=${VHW_INPUT:-none} +fi + +if [ "$TRANSPORT" = "vp8channel" ]; then + echo "" + echo "--- VP8channel settings ---" + + read -p "VP8 FPS [default: 25]: " VP8FPS_INPUT + VP8_FPS=${VP8FPS_INPUT:-25} + + read -p "VP8 batch size (frames per tick) [default: 1]: " VP8BATCH_INPUT + VP8_BATCH=${VP8BATCH_INPUT:-1} +fi + +if [ "$TRANSPORT" = "seichannel" ]; then + echo "" + echo "--- SEIchannel settings ---" + + read -p "SEI FPS [default: 60]: " SEIFPS_INPUT + SEI_FPS=${SEIFPS_INPUT:-60} + + read -p "SEI batch size (frames per tick) [default: 64]: " SEIBATCH_INPUT + SEI_BATCH=${SEIBATCH_INPUT:-64} + + read -p "SEI fragment size in bytes [default: 900]: " SEIFRAG_INPUT + SEI_FRAG=${SEIFRAG_INPUT:-900} + + read -p "SEI ACK timeout in milliseconds [default: 2000]: " SEIACK_INPUT + SEI_ACK=${SEIACK_INPUT:-2000} +fi + +echo "" +echo "[*] Cleaning workspace..." +rm -rf "$WORK_DIR" +mkdir -p "$WORK_DIR" + +CACHE_DIR="${OLCRTC_CACHE_DIR:-$HOME/.cache/olcrtc}" +GOMOD_CACHE="$CACHE_DIR/gomod" +GO_BUILD_CACHE="$CACHE_DIR/gobuild" + +if [ "$NO_CACHE" = "1" ]; then + echo "[*] --no-cache: purging Go cache at $CACHE_DIR" + chmod -R u+w "$GOMOD_CACHE" "$GO_BUILD_CACHE" 2>/dev/null || true + if ! rm -rf "$GOMOD_CACHE" "$GO_BUILD_CACHE" 2>/dev/null; then + echo "[*] Falling back to in-container purge (files owned by container UID)..." + podman run --rm \ + -v "$CACHE_DIR":/cache:Z \ + "$IMAGE_NAME" \ + sh -c 'rm -rf /cache/gomod /cache/gobuild' + fi +fi + +mkdir -p "$GOMOD_CACHE" "$GO_BUILD_CACHE" +echo "[*] Using Go cache: $CACHE_DIR" + +echo "[*] Cloning repository..." +git clone --depth 1 --recurse-submodules --branch "$BRANCH" "$REPO_URL" "$WORK_DIR" + +echo "[*] Pulling Go image..." +podman pull "$IMAGE_NAME" + +echo "[*] Building OlcRTC..." +podman run --rm \ + --network host \ + -v "$WORK_DIR":/app:Z \ + -v "$GOMOD_CACHE":/go/pkg/mod:Z \ + -v "$GO_BUILD_CACHE":/root/.cache/go-build:Z \ + -w /app \ + "$IMAGE_NAME" \ + sh -c "go mod download && go build -trimpath -ldflags='-s -w' -o olcrtc ./cmd/olcrtc" + +if [ ! -f "$WORK_DIR/olcrtc" ]; then + echo "[X] Build failed" + exit 1 +fi + +if [ "$GEN_ROOM" = "1" ]; then + echo "[*] Generating room via mode: gen..." + GEN_CONFIG="$WORK_DIR/gen.yaml" + cat > "$GEN_CONFIG" < "$KEY_FILE" + chmod 600 "$KEY_FILE" + echo "" + echo "==========================================" + echo "NEW ENCRYPTION KEY (saved to $KEY_FILE):" + echo "$KEY" + echo "==========================================" + echo "" +fi + +# Generate YAML config +CONFIG_FILE="$WORK_DIR/server.yaml" +cat > "$CONFIG_FILE" <> "$CONFIG_FILE" <> "$CONFIG_FILE" <> "$CONFIG_FILE" <> "$CONFIG_FILE" <> "$CONFIG_FILE" </dev/null; then + TRANSPORT_PAYLOAD="" + else + TRANSPORT_PAYLOAD="" + fi +fi + +OLC_URI="olcrtc://$CARRIER?${TRANSPORT}${TRANSPORT_PAYLOAD}@$ROOM_ID#$KEY\$$sub_configname" +echo "uri: $OLC_URI" +echo "" + +GR_BIN="$WORK_DIR/gr" +OS=$(uname -s | tr '[:upper:]' '[:lower:]') +ARCH=$(uname -m) +case "$ARCH" in + x86_64) ARCH="amd64" ;; + aarch64|arm64) ARCH="arm64" ;; +esac +GR_URL="https://github.com/zarazaex69/gr/releases/latest/download/gr-${OS}-${ARCH}" + +if curl -fsSL "$GR_URL" -o "$GR_BIN" 2>/dev/null; then + chmod +x "$GR_BIN" + echo "[*] QR code for your URI (scan with olcbox):" + echo "" + "$GR_BIN" -o -s "$OLC_URI" 2>/dev/null || echo "[!] QR generation failed" + echo "" +else + echo "[!] Could not download gr ($GR_URL), skipping QR" +fi + +if [ -n "$SOCKS_PROXY_ADDR" ]; then + echo "SOCKS5 proxy: $SOCKS_PROXY_ADDR:$SOCKS_PROXY_PORT" +fi + +echo "" +echo "View logs:" +echo " podman logs -f $CONTAINER_NAME" +echo "" +echo "Stop server:" +echo " podman stop $CONTAINER_NAME" +echo ""