#!/usr/bin/env bash # notify-developer.sh — envía DM a los desarrolladores al crear un bot/agente # # El propio bot recién creado envía un mensaje de bienvenida enriquecido con: # - Nombre y tipo (agent/robot) # - Descripción (leída de config.yaml) # - Tools habilitadas (si es agent con tools) # - Instrucciones de uso # # Reintenta hasta 3 veces con backoff si el envío falla. # # Uso: # ./dev-scripts/agent/notify-developer.sh # # Requisitos en .env: # DEVELOPER_MATRIX_USERS — lista separada por comas de usernames Matrix # Ejemplo: DEVELOPER_MATRIX_USERS=egutierrez,admin # MATRIX_TOKEN_ — token del bot recién creado # MATRIX_HOMESERVER, MATRIX_SERVER_NAME source "$(dirname "$0")/../_common.sh" load_env ID="${1:-}" TYPE="${2:-agent}" DISPLAYNAME="${3:-$ID}" NORM="$(normalize_id "$ID")" [[ -z "$ID" ]] && { warn "notify-developer: se necesita agent-id"; exit 0; } # ── Obtener token del bot ──────────────────────────────────────────────── TOKEN_VAR="MATRIX_TOKEN_${NORM}" TOKEN="${!TOKEN_VAR:-}" if [[ -z "$TOKEN" ]]; then warn "notify-developer: $TOKEN_VAR no encontrado en .env — saltando notificación" exit 0 fi # ── Obtener lista de desarrolladores ───────────────────────────────────── if [[ -z "${DEVELOPER_MATRIX_USERS:-}" ]]; then warn "notify-developer: DEVELOPER_MATRIX_USERS no definido en .env — saltando" exit 0 fi # ── Leer descripcion del config.yaml ──────────────────────────────────── CONFIG_FILE="" for candidate in "agents/${ID}/config.yaml" "agents/_specials/${ID}/config.yaml"; do if [[ -f "$candidate" ]]; then CONFIG_FILE="$candidate" break fi done DESCRIPTION="" TOOLS_LIST="" if [[ -n "$CONFIG_FILE" ]]; then # Extraer descripcion (entre comillas si las tiene) DESCRIPTION=$(grep -m1 '^\s*description:' "$CONFIG_FILE" | sed 's/.*description:\s*"\?\(.*\)"\?$/\1/' | sed 's/"$//') # Extraer tools habilitadas (buscar lineas "enabled: true" dentro de secciones de tools) if grep -q 'tool_use:' "$CONFIG_FILE" 2>/dev/null; then TOOL_USE_ENABLED=$(awk '/tool_use:/,/^[^ ]/' "$CONFIG_FILE" | grep -m1 'enabled:' | awk '{print $2}') if [[ "$TOOL_USE_ENABLED" == "true" ]]; then # Listar secciones de tools habilitadas TOOLS_LIST=$(awk '/^tools:/,/^[a-z]/' "$CONFIG_FILE" | grep -B1 'enabled: true' | grep -v 'enabled' | grep -v '^--$' | sed 's/://g' | xargs 2>/dev/null || true) fi fi fi # ── Construir mensaje enriquecido ──────────────────────────────────────── if [[ "$TYPE" == "robot" ]]; then EMOJI="🤖" TYPE_LABEL="Robot" USAGE_MSG="Mis comandos built-in: \`help\`, \`ping\`, \`status\`, \`info\`, \`version\`." USAGE_MSG="${USAGE_MSG}\nEscribeme directamente con un comando (sin prefijo \`!\`)." else EMOJI="🧠" TYPE_LABEL="Agente" USAGE_MSG="Escríbeme por DM o mencioname en un room." USAGE_MSG="${USAGE_MSG}\nUsa \`!help\` para ver mis comandos disponibles." fi # Construir mensaje markdown MSG="${EMOJI} **¡Hola! Soy ${DISPLAYNAME}** (${TYPE_LABEL})" MSG="${MSG}\n" if [[ -n "$DESCRIPTION" ]]; then MSG="${MSG}\n${DESCRIPTION}" MSG="${MSG}\n" fi if [[ -n "$TOOLS_LIST" ]]; then MSG="${MSG}\n**Herramientas:** ${TOOLS_LIST}" MSG="${MSG}\n" fi MSG="${MSG}\n${USAGE_MSG}" # ── Funcion de envio con reintentos ────────────────────────────────────── send_dm() { local user_id="$1" local max_retries=3 local retry=0 local backoff=2 while [[ $retry -lt $max_retries ]]; do # Crear DM room con E2EE habilitado (o reutilizar existente) ROOM_RESP=$(curl -sf -X POST "${MATRIX_HOMESERVER}/_matrix/client/v3/createRoom" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"is_direct\": true, \"invite\": [\"${user_id}\"], \"preset\": \"trusted_private_chat\", \"initial_state\": [ { \"type\": \"m.room.encryption\", \"state_key\": \"\", \"content\": { \"algorithm\": \"m.megolm.v1.aes-sha2\" } } ] }" 2>&1) || { retry=$((retry + 1)) if [[ $retry -lt $max_retries ]]; then warn " Intento $retry/$max_retries fallo al crear room con $user_id — reintentando en ${backoff}s..." sleep "$backoff" backoff=$((backoff * 2)) continue fi warn " No se pudo crear DM room con $user_id tras $max_retries intentos" return 1 } ROOM_ID=$(echo "$ROOM_RESP" | grep -o '"room_id":"[^"]*"' | cut -d'"' -f4) if [[ -z "$ROOM_ID" ]]; then retry=$((retry + 1)) if [[ $retry -lt $max_retries ]]; then warn " Respuesta inesperada — reintentando en ${backoff}s..." sleep "$backoff" backoff=$((backoff * 2)) continue fi warn " Respuesta inesperada al crear room: $ROOM_RESP" return 1 fi # Enviar mensaje con formato markdown TXN_ID="notify-$(date +%s%N)" # Escapar newlines para el JSON MSG_ESCAPED=$(echo -e "$MSG") MSG_JSON=$(echo -e "$MSG" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read().rstrip()))" 2>/dev/null || echo "\"${MSG}\"") SEND_RESP=$(curl -sf -X PUT \ "${MATRIX_HOMESERVER}/_matrix/client/v3/rooms/${ROOM_ID}/send/m.room.message/${TXN_ID}" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"msgtype\": \"m.text\", \"body\": ${MSG_JSON}, \"format\": \"org.matrix.custom.html\", \"formatted_body\": ${MSG_JSON} }" 2>&1) || { retry=$((retry + 1)) if [[ $retry -lt $max_retries ]]; then warn " Intento $retry/$max_retries fallo al enviar mensaje — reintentando en ${backoff}s..." sleep "$backoff" backoff=$((backoff * 2)) continue fi warn " No se pudo enviar mensaje a $user_id tras $max_retries intentos" return 1 } ok "DM enviado a $user_id" return 0 done } # ── Enviar DM a cada desarrollador ─────────────────────────────────────── IFS=',' read -ra DEVS <<< "$DEVELOPER_MATRIX_USERS" for dev in "${DEVS[@]}"; do dev="$(echo "$dev" | xargs)" # trim spaces [[ -z "$dev" ]] && continue # Acepta ambos formatos: # - "egutierrez" (bare username) # - "@egutierrez:matrix-...organic-machine.com" (full MXID) if [[ "$dev" == @*:* ]]; then USER_ID="$dev" else USER_ID="@${dev}:${MATRIX_SERVER_NAME}" fi info "Enviando DM de $ID a $USER_ID..." send_dm "$USER_ID" done