文件预览

maxbank.sh

查看 Max Banking 技能包中的文件内容。

文件内容

scripts/maxbank.sh

#!/usr/bin/env bash
# Wrapper unificado para todas as ações do Max Banking.
# Uso: bash maxbank.sh <acao> [args...]
#
# Ações:
#   saldo                         — consulta saldo via MCP
#   conta                         — exibe dados da conta conectada
#   setup <codigo> <env> [url]    — conecta conta (pareamento + proxy)
#   pix-validate-qr CODIGO_QR    — valida QR Code PIX (retorna has_amount, amount, can_modify_final_amount)
#                                 O EMV pode conter espaços: passe o código inteiro entre aspas, ex.:
#                                 pix-validate-qr '00020126...Nome Com Espaço...6304'
#   pix CHAVE VALOR               — PIX por chave (valor obrigatório)
#   pix CODIGO_QR [VALOR]         — PIX por QR copia e cola (valor obrigatório se QR não tem valor embutido);
#                                 QR com espaços: aspas em volta do código, ex. pix '00020126...' 10.50
#   pix code=... amount:N         — formato nomeado
#   billet LINHA_DIGITAVEL        — cria pagamento de boleto (um único argumento: a linha)
#   billet code=LINHA_DIGITAVEL   — formato nomeado (apenas code=; sem outros parâmetros)
#   status                        — exibe status da configuração mcporter

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SESSION_FILE="$HOME/.openclaw/secrets/maxbank/session.json"
# Função para verificar que mcporter está configurado
check_mcporter_config() {
  # Se não há session, retorna erro
  if [[ ! -f "$SESSION_FILE" ]]; then
    echo "[maxbank] ERRO: Nenhuma conta configurada. Execute: bash maxbank.sh setup <codigo> [env]" >&2
    return 1
  fi

  # Testa se mcporter está instalado
  if ! command -v mcporter &>/dev/null; then
    echo "[maxbank] ERRO: mcporter não está instalado. Execute: npm install -g mcporter" >&2
    return 1
  fi

  # Verifica se o servidor banking está configurado no mcporter
  if ! mcporter config list 2>/dev/null | grep -q banking; then
    echo "[maxbank] AVISO: servidor 'banking' não encontrado no mcporter. Reconfigurando..." >&2

    local AGENT_KEY=""
    local AGENT_KEY_FILE="$HOME/.openclaw/secrets/maxbank/agent_key"
    if [[ -f "$AGENT_KEY_FILE" ]]; then
      AGENT_KEY=$(cat "$AGENT_KEY_FILE" 2>/dev/null)
    fi

    local MCP_URL=$(node -e "
      try {
        const s = JSON.parse(require('fs').readFileSync('$SESSION_FILE', 'utf8'));
        console.log(s.mcp_base_url || '');
      } catch(e) { console.log(''); }
    " 2>/dev/null)

    if [[ -z "$AGENT_KEY" || -z "$MCP_URL" ]]; then
      echo "[maxbank] ERRO: agent_key ou MCP_URL ausente. Execute: bash maxbank.sh setup <codigo> [env]" >&2
      return 1
    fi

    mcporter config add banking \
      --http-url "$MCP_URL" \
      --header "{\"Authorization\": \"Bearer $AGENT_KEY\"}" 2>/dev/null \
      || {
        echo "[maxbank] ERRO: Falha ao configurar mcporter. Execute: bash maxbank.sh setup <codigo> [env]" >&2
        return 1
      }
    echo "[maxbank] mcporter reconfigurado: banking → $MCP_URL" >&2
  fi
}

ACTION="${1:?Uso: bash maxbank.sh <acao> [args...]
Ações disponíveis: saldo, conta, setup, pix, billet, status}"
shift

# Garante configuração antes de qualquer chamada MCP
if [[ "$ACTION" != "setup" && "$ACTION" != "connect" && "$ACTION" != "conectar" && "$ACTION" != "configurar" && "$ACTION" != "instalar" && "$ACTION" != "auth" && "$ACTION" != "login" ]]; then
  check_mcporter_config || exit 1
fi

case "$ACTION" in
  saldo|balance|get-balance)
    echo "[maxbank] Chamando: mcporter call banking.banking_get_balance" >&2
    mcporter call banking.banking_get_balance --output json
    ;;
  conta|account|info|sessao|session)
    if [[ ! -f "$SESSION_FILE" ]]; then
      echo "NO_ACCOUNT"
      exit 0
    fi
    node -e "
      const s = JSON.parse(require('fs').readFileSync('$SESSION_FILE', 'utf8'));
      console.log('ACCOUNT_OK');
      console.log('PHONE=' + (s.phone_number || ''));
      console.log('ENV=' + (s.environment || ''));
      console.log('MCP_URL=' + (s.mcp_base_url || ''));
      console.log('PAIRED_AT=' + (s.paired_at || ''));
    " 2>/dev/null || { echo "NO_ACCOUNT"; exit 0; }
    ;;
  setup|connect|conectar|configurar|instalar|auth|login)
    bash "$SCRIPT_DIR/setup.sh" "$@"
    ;;
  pix-validate-qr|validar-qr)
    CODE=""
    for arg in "$@"; do
      if [[ "$arg" == code=* ]]; then
        CODE="${arg#code=}"
      elif [[ -z "$CODE" ]]; then
        CODE="$arg"
      fi
    done
    CODE="${CODE#"${CODE%%[![:space:]]*}"}"
    if [[ -z "$CODE" ]]; then
      echo "PIX_VALIDATE_QR_INVALID_ARGS: precisa do código QR PIX copia e cola."
      exit 1
    fi
    if [[ ! "$CODE" =~ ^00020 ]]; then
      echo "PIX_VALIDATE_QR_INVALID_ARGS: código informado não é QR Code PIX (deve começar com 00020)."
      exit 1
    fi
    echo "[maxbank] Chamando: mcporter call banking.banking_pix_qrcode_validate code=..." >&2
    mcporter call banking.banking_pix_qrcode_validate "code=$CODE" --output json
    ;;
  pix)
    PIX_ARGS=()
    HAS_NAMED=false
    for arg in "$@"; do
      [[ "$arg" == code=* || "$arg" == amount=* ]] && HAS_NAMED=true
    done
    if [[ "$HAS_NAMED" == true ]]; then
      for arg in "$@"; do
        if [[ "$arg" == amount=* ]]; then
          VAL="${arg#amount=}"
          VAL="${VAL//,/.}"
          PIX_ARGS+=("amount:$VAL")
        else
          PIX_ARGS+=("$arg")
        fi
      done
    else
      CODE=""
      AMOUNT=""
      for arg in "$@"; do
        if [[ "$arg" == *@* ]] || [[ "$arg" =~ ^00020 ]] || [[ "$arg" =~ ^[0-9]{10,11}$ ]]; then
          CODE="$arg"
        elif [[ -n "$arg" && ${#arg} -gt 10 ]]; then
          CODE="$arg"
        elif [[ "$arg" =~ ^[0-9]+([.,][0-9]+)?$ ]]; then
          AMOUNT="$arg"
        fi
      done
      if [[ -z "$CODE" ]]; then
        echo "PIX_INVALID_ARGS: precisa de chave PIX ou código QR (copia e cola)."
        exit 1
      fi
      if [[ "$CODE" =~ ^00020 ]]; then
        PIX_ARGS=(code="$CODE")
        if [[ -n "$AMOUNT" ]]; then
          AMOUNT="${AMOUNT//,/.}"
          PIX_ARGS+=("amount:$AMOUNT")
        fi
      else
        if [[ -z "$AMOUNT" ]]; then
          echo "PIX_INVALID_ARGS: para chave PIX é obrigatório informar o valor."
          exit 1
        fi
        AMOUNT="${AMOUNT//,/.}"
        PIX_ARGS=(code="$CODE" "amount:$AMOUNT")
      fi
    fi
    echo "[maxbank] Chamando: mcporter call banking.banking_pix ${PIX_ARGS[*]}" >&2
    mcporter call banking.banking_pix "${PIX_ARGS[@]}"
    ;;
  billet|boleto)
    # A API banking_billet aceita apenas UM argumento: code=<linha digitável completa>.
    # Se o LLM repassar vários parâmetros nomeados (ex.: code= + amount=), o mcporter envia
    # campos a mais e o backend responde 422.
    if [[ $# -lt 1 ]]; then
      echo "BILLET_INVALID_ARGS: precisa da linha digitável. Exemplos:"
      echo "  billet code=\"23793.38...\""
      echo "  billet 23793.38128 60000.000003..."
      exit 1
    fi

    HAS_CODE_NAMED=false
    for arg in "$@"; do
      [[ "$arg" == code=* ]] && HAS_CODE_NAMED=true && break
    done

    BILLET_SINGLE=""
    if [[ "$HAS_CODE_NAMED" == true ]]; then
      CODE_BUF=""
      SEEN_CODE=false
      for arg in "$@"; do
        if [[ "$arg" == code=* ]]; then
          if [[ "$SEEN_CODE" == true ]]; then
            echo "BILLET_TOO_MANY_ARGS: o boleto aceita apenas UM parâmetro para a API: a linha digitável em code= (sem repetir code=; junte a linha inteira em um único code= ou use aspas)."
            exit 1
          fi
          SEEN_CODE=true
          CODE_BUF="${arg#code=}"
        elif [[ "$arg" == *"="* ]]; then
          echo "BILLET_TOO_MANY_ARGS: o pagamento de boleto usa somente a linha digitável. Não envie outros parâmetros (valor, amount, descrição, etc.). Apenas: code=<linha> ou um único argumento posicional com a linha inteira."
          exit 1
        else
          if [[ "$SEEN_CODE" != true ]]; then
            echo "BILLET_INVALID_ARGS: com code= nomeado, o primeiro argumento deve ser code=<linha digitável>. Demais tokens só podem continuar a linha após o primeiro code=. Use posicional: billet LINHA (várias palavras) se preferir."
            exit 1
          fi
          CODE_BUF="$CODE_BUF $arg"
        fi
      done
      CODE_BUF="${CODE_BUF#"${CODE_BUF%%[![:space:]]*}"}"
      BILLET_SINGLE="code=$CODE_BUF"
    else
      # Posicional: um único argumento code= com toda a linha (espaços preservados em $*)
      BILLET_SINGLE="code=$*"
    fi

    echo "[maxbank] Chamando: mcporter call banking.banking_billet $BILLET_SINGLE" >&2
    mcporter call banking.banking_billet "$BILLET_SINGLE"
    ;;
  debug-token)
    echo "[maxbank] Chamando: mcporter call banking.banking_debug_token" >&2
    mcporter call banking.banking_debug_token --output json
    ;;
  proxy-status|status-proxy|check-proxy|config-status|status)
    echo "=== MaxBank Config Status ==="
    if [[ ! -f "$SESSION_FILE" ]]; then
      echo "Status: NO SESSION ($SESSION_FILE not found)"
      exit 1
    fi

    echo ""
    echo "=== mcporter config ==="
    if command -v mcporter &>/dev/null; then
      mcporter config list 2>/dev/null || echo "mcporter config list failed"
    else
      echo "mcporter NOT INSTALLED"
    fi

    echo ""
    echo "=== session.json ==="
    if [[ -f "$SESSION_FILE" ]]; then
      node -e "
        const s = JSON.parse(require('fs').readFileSync('$SESSION_FILE', 'utf8'));
        console.log(JSON.stringify(s, null, 2));
      " 2>/dev/null || cat "$SESSION_FILE"
    fi

    echo ""
    echo "=== agent_key ==="
    AGENT_KEY_FILE="$HOME/.openclaw/secrets/maxbank/agent_key"
    if [[ -f "$AGENT_KEY_FILE" ]]; then
      node -e "
        const k = require('fs').readFileSync('$AGENT_KEY_FILE', 'utf8').trim();
        const parts = k.split('.');
        if (parts.length >= 2) {
          console.log(parts[0] + '.' + parts[1].slice(0, 4) + '...' + ' (length: ' + k.length + ')');
        } else {
          console.log(k.slice(0, 8) + '...' + ' (length: ' + k.length + ')');
        }
      " 2>/dev/null || echo "EXISTS (could not read)"
    else
      echo "NOT FOUND: $AGENT_KEY_FILE"
    fi
    ;;
  *)
    echo "Ação desconhecida: $ACTION"
    echo "Ações disponíveis: saldo, conta, setup, pix-validate-qr, pix, billet, debug-token, status"
    exit 1
    ;;
esac