WebSockets establecen una conexión continua entre el cliente y el servidor, permitiendo comunicación bidireccional. Chatwoot utiliza esta conexión para proporcionar actualizaciones en tiempo real sobre eventos de la plataforma. Para conectarte al WebSocket de Chatwoot, simplemente proporciona un token y sigue las instrucciones de configuración detalladas en esta guía.

**Nota**: Esta función es experimental y la documentación puede cambiar con cada lanzamiento. Además, no se puede garantizar la compatibilidad retroactiva, por lo que es importante asegurarse de estar utilizando la versión más reciente de la implementación.

## ¿Por qué debería usar una conexión WebSocket?

Una conexión WebSocket permite actualizaciones de datos en tiempo real, lo que la hace ideal para clientes como un SDK de cliente de Android o iOS para Chatwoot. Esto ayuda a actualizar el dashboard sin necesidad de recargar la página. Por lo tanto, puede mejorar la experiencia del usuario y aumentar la productividad de un agente.

## ¿Cómo configurar una conexión WebSocket con Chatwoot?

Para configurar una conexión WebSocket con Chatwoot, necesitas iniciar una conexión con el token de autenticación PubSub proporcionado por Chatwoot. La URL para la conexión es `wss://<your-installation-url>/cable`. Si utilizas Chatwoot Cloud, puedes usar `wss://app.chatwoot.com/cable` como URL.

> Un token PubSub es un token que se utiliza para autenticar a un cliente al conectarse a un servicio PubSub (publicar-suscribir). El cliente debe presentar este token al servicio para poder establecer una conexión y comenzar a publicar o suscribirse a mensajes.

Hay dos tipos de tokens PubSub disponibles en Chatwoot, como se indica a continuación.

1. **Token PubSub de Usuario**: Este token tiene los privilegios de un agente/admin y recibiría todos los eventos listados más adelante en la página. Puedes obtener el token PubSub llamando a la [API de Perfil](https://www.chatwoot.com/developers/api/#operation/fetchProfile).

2. **Token PubSub de Contacto**: Chatwoot genera un token PubSub único para cada sesión de un contacto. Este token se puede usar para conectarse al WebSocket y recibir actualizaciones en tiempo real para la misma sesión. Cuando un contacto es creado mediante las APIs públicas, el `pubsub_token` se incluye en la respuesta. Este token sólo otorga acceso a eventos relacionados con la sesión actual, como `conversation.created`, `conversation.status_changed`, `message.created`, `message.updated`, `conversation_typing_on`, `conversation_typing_off` y `presence.update`.

Por favor, consulta [APIs de Cliente](https://www.chatwoot.com/docs/product/channels/api/client-apis) para construir integraciones en tiempo real orientadas al cliente usando Chatwoot.

**Nota**: Este token puede rotarse regularmente según el tipo de instalación. Por favor, asegúrate de estar utilizando el token más actualizado.

### ¿Cómo conectarse al WebSocket de Chatwoot?

Para conectarte al WebSocket de Chatwoot, usa el comando `subscribe` e incluye tu `pubSubToken`, `accountId` y `userId` (si usas un token de usuario) en la solicitud de conexión. Aquí tienes un ejemplo de cómo puedes conectar con Chatwoot.

```
// Agrega un método auxiliar para convertir JSON a una cadena
const stringify = (payload = {}) => JSON.stringify(payload);

const pubSubToken = "<contact/user-pub-sub-token>";
const accountId = "<your-account-id-in-integer>";
const userId = "<user-id-in-integer-if-using-user-token>";
const connection = new WebSocket(
  "wss://app.chatwoot.com/cable"
);

connection.send(
  stringify({
    command: "subscribe",
    identifier: stringify({
      channel: "RoomChannel",
      pubsub_token: pubSubToken,
      account_id: accountId,
      user_id: userId,
    }),
  })
);

// La cadena esperada en connection.send es del formato:
// {"command":"subscribe","identifier":"{\\"channel\\":\\"RoomChannel\\",\\"pubsub_token\\":\\"your-pubsub-token\\",\\"account_id\\": account_id_integer,\\"user_id\\":user_id_integer }"}
```

### Publicar presencia al servidor WebSocket

Para mantener el estado en línea de tus usuarios en Chatwoot, puedes enviar un evento de actualización de presencia a Chatwoot cada 30 segundos. Esta acción mantendrá el estado del agente/contacto en línea.

**¿Cómo actualizar la presencia de un agente/admin?**

Para actualizar la presencia de un agente o administrador, envía la siguiente carga útil al servidor:

```
const userPayload = stringify({
  command: "message",
  identifier: stringify({
    channel: "RoomChannel",
    pubsub_token: "<user-pubsub-token>",
    account_id: accountId,
    user_id: userId,
  }),
  data: stringify({ action: "update_presence" }),
});

connection.send(userPayload);
// La cadena esperada en connection.send es del formato:
// {"command":"message","identifier":"{\\"channel\\":\\"RoomChannel\\",\\"pubsub_token\\":\\"your-pubsub-token\\",\\"account_id\\": account_id_integer,\\"user_id\\":user_id_integer ","data":"{\\"action\\":\\"update_presence\\"}"}
```

**¿Cómo actualizar la presencia de un contacto?**

Para actualizar la presencia de un contacto, envía la siguiente carga útil al servidor:

```
const agentPayload = stringify({
  command: "message",
  identifier: stringify({
    channel: "RoomChannel",
    pubsub_token: "<user-pubsub-token>",
  }),
  data: stringify({ action: "update_presence" }),
});

connection.send(agentPayload);
// La cadena esperada en connection.send es del formato:
// {"command":"message","identifier":"{\\"channel\\":\\"RoomChannel\\",\\"pubsub_token\\":\\"your-pubsub-token\\","data":"{\\"action\\":\\"update_presence\\"}"}
```

## Carga útil de WebSocket

### Objetos

Un evento puede contener cualquiera de los siguientes objetos como payload. Los diferentes tipos de objetos soportados en Chatwoot son los siguientes.

**Conversación**

La siguiente carga útil será devuelta para una conversación.

```
{
  "additional_attributes": {
    "browser": {
      "device_name": "string",
      "browser_name": "string",
      "platform_name": "string",
      "browser_version": "string",
      "platform_version": "string"
    },
    "referer": "string",
    "initiated_at": {
      "timestamp": "iso-datetime"
    }
  },
  "can_reply": "boolean",
  "channel": "string",
  "id": "integer",
  "inbox_id": "integer",
  "contact_inbox": {
    "id": "integer",
    "contact_id": "integer",
    "inbox_id": "integer",
    "source_id": "string",
    "created_at": "datetime",
    "updated_at": "datetime",
    "hmac_verified": "boolean"
  },
  "messages": ["Array of message objects"],
  "meta": {
    "sender": {
      // Objeto Contacto
    },
    "assignee": {
      // Objeto Usuario
    }
  },
  "status": "string",
  "unread_count": "integer",
  "agent_last_seen_at": "unix-timestamp",
  "contact_last_seen_at": "unix-timestamp",
  "timestamp": "unix-timestamp",
  "account_id": "integer"
}
```

**Contacto**

La siguiente carga útil será devuelta para un contacto.

```
{
  "additional_attributes": "object",
  "custom_attributes": "object",
  "email": "string",
  "id": "integer",
  "identifier": "string or null",
  "name": "string",
  "phone_number": "string or null",
  "thumbnail": "string"
}
```

**Usuario**

La siguiente carga útil será devuelta para un agente/admin.

```
{
  "id": "integer",
  "name": "string",
  "available_name": "string",
  "avatar_url": "string",
  "availability_status": "string",
  "thumbnail": "string"
}
```

**Mensaje**

La siguiente carga útil será devuelta para un mensaje.

```
{
  "id": "integer",
  "content": "string",
  "account_id": "integer",
  "inbox_id": "integer",
  "message_type": "integer",
  "created_at": "unix-timestamp",
  "updated_at": "datetime",
  "private": "boolean",
  "status": "string",
  "source_id": "string / null",
  "content_type": "string",
  "content_attributes": "object",
  "sender_type": "string",
  "sender_id": "integer",
  "external_source_ids": "object",
  "sender": {
    "type": "string - contact/user"
    // Objeto Usuario o Contacto
  }
}
```

**Notificación**

La siguiente carga útil será devuelta para una notificación.

```
{
  "id": "integer",
  "notification_type": "string",
  "primary_actor_type": "string",
  "primary_actor_id": "integer",
  "primary_actor": {
    "can_reply": "boolean",
    "channel": "string",
    "id": "integer",
    "inbox_id": "integer",
    "meta": {
      "assignee": {
        "id": "integer",
        "name": "string",
        "available_name": "string",
        "avatar_url": "string",
        "type": "user",
        "availability_status": "string",
        "thumbnail": "string"
      },
      "hmac_verified": "boolean"
    },
    "agent_last_seen_at": "unix-timestamp",
    "contact_last_seen_at": "unix-timestamp",
    "timestamp": "unix-timestamp",
  },
  "read_at": "unix-timestamp",
  "secondary_actor": "object/null",
  "created_at":"unix-timestamp",
  "account_id": "integer",
  "push_message_title": "string"
}
```

### Identificador

Cada evento tendrá un atributo `identifier` en el siguiente formato.

```
{
  "identifier": "{\\"channel\\":\\"RoomChannel\\",\\"pubsub_token\\":\\"token\\",\\"account_id\\":id,\\"user_id\\":user_id}"
}
```

### Mensaje

Cada evento incluirá un atributo `message` que contiene el nombre del evento así como los datos asociados. Para consultar la lista de eventos, revisa la documentación a continuación.

## Tipos de Eventos

### conversation.created

Este evento se activa cuando se inicia una nueva conversación. Si te suscribes al token PubSub del contacto, este evento solo incluirá datos relacionados con la sesión específica asociada al token PubSub.

**Disponible para**: agente/admin, contacto

```
{
  "message": {
    "event": "conversation.created",
    "data": {
      // El objeto Conversación estará disponible aquí
    }
  }
}
```

### conversation.read

Este evento se activa y se envía a los agentes/admins que tienen acceso a la bandeja de entrada, cuando un contacto ha leído un mensaje.

**Disponible para**: agente/admin

```
{
  "message": {
    "event": "conversation.read",
    "data": {
      // El objeto Conversación estará disponible aquí
    }
  }
}
```

### message.created

Este evento se activa y se envía a los agentes, admins y contactos cuando se crea un nuevo mensaje en una conversación a la que tienen acceso.

**Disponible para**: agente/admin, contacto

```
{
  "message": {
    "event": "message.created",
    "data": {
      // El objeto Mensaje estará disponible aquí
    }
  }
}
```

### message.updated

Este evento se activa y se envía a los agentes, admins y contactos cuando un mensaje es actualizado en una conversación a la que tienen acceso.

**Disponible para**: agente/admin, contacto

```
{
  "message": {
    "event": "message.updated",
    "data": {
      // El objeto Mensaje estará disponible aquí
    }
  }
}
```

### conversation.status_changed

Este evento se envía a los agentes, admins y contactos cuando el estado de una conversación es actualizado.

**Disponible para**: agente/admin, contacto

```
{
  "message": {
    "event": "conversation.status_changed",
    "data": {
      // El objeto Conversación estará disponible aquí
    }
  }
}
```

### conversation.typing_on

Este evento se envía a los agentes, admins y contactos cuando un contacto o un agente comienza a escribir una respuesta.

**Disponible para**: agente/admin, contacto

```
{
  "message": {
    "event": "conversation.typing_on",
    "data": {
      "conversation": {
        // El objeto Conversación estará disponible aquí
      },
      "user": {
        // El objeto Usuario Contacto/Agente/Admin estará disponible aquí.
      },
      "is_private": "boolean", // Indica si el agente está escribiendo una nota privada o no.
      "account_id": "integer"
    }
  }
}
```

### conversation.typing_off

Este evento se envía a los agentes, admins y contactos cuando un contacto o un agente termina de escribir una respuesta.

**Disponible para**: agente/admin, contacto

```
{
  "message": {
    "event": "conversation.typing_off",
    "data": {
      "conversation": {
        // El objeto Conversación estará disponible aquí
      },
      "user": {
        // El objeto Contacto/Usuario estará disponible aquí.
      },
      "account_id": "integer"
    }
  }
}
```

### assignee.changed

Este evento se envía a los agentes/admins con acceso a una bandeja de entrada cuando el agente asignado es cambiado.

**Disponible para**: agente/admin

```
{
  "message": {
    "event": "assignee.changed",
    "data": {
      // El objeto Conversación estará disponible aquí
    }
  }
}
```

### team.changed

Este evento se envía a los agentes/admins con acceso a una bandeja de entrada cuando el equipo asignado es cambiado.

**Disponible para**: agente/admin

```
{
  "message": {
    "event": "team.changed",
    "data": {
      // El objeto Conversación estará disponible aquí
    }
  }
}
```

### conversation.contact_changed

Este evento se envía a los agentes/admins cuando dos contactos son fusionados y todas sus conversaciones se consolidan bajo un solo contacto.

**Disponible para**: agente/admin

```
{
  "message": {
    "event": "conversation.contact_changed",
    "data": {
      // El objeto Conversación estará disponible aquí
    }
  }
}
```

### contact.created

Este evento se envía a los agentes/admins cuando un contacto es creado.

**Disponible para**: agente/admin

```
{
  "message": {
    "event": "contact.created",
    "data": {
      // El objeto Contacto estará disponible aquí
    }
  }
}
```

### contact.updated

Este evento se envía a los agentes/admins cuando un contacto es actualizado.

**Disponible para**: agente/admin

```
{
  "message": {
    "event": "contact.updated",
    "data": {
      // El objeto Contacto estará disponible aquí
    }
  }
}
```

### presence.update

Disponible para agente y contacto, este evento proporciona actualizaciones en tiempo real sobre el estado de disponibilidad de los usuarios en el sistema. El evento entregado a los contactos no incluirá información sobre el estado de disponibilidad de otros contactos.

**Disponible para**: agente/admin

```
{
  "message": {
    "event": "presence.update",
    "data": {
      "account_id": "integer",
      "users": {
        "user-id": "string"
      },
      "contacts": {
        "contact-id": "string"
      }
    }
  }
}
```

### notification_created

Este evento se envía a los agentes/admins cuando se crea una notificación.

**Disponible para**: agente/admin