# Widget de chat

Instale no seu site, assine a identidade dos seus usuários e controle com window.Domu.

Guia JavaScript Sem chave de API

## Instale no seu site

Copie o código em Configurações › Chat web (widget) e cole antes de fechar o body. `data-key` é a chave pública do widget; `data-locale` fixa o idioma (es, en, pt); `data-property` e `data-property-code` dizem de qual imóvel a pessoa escreve. Seu domínio precisa estar nos permitidos do widget.

```html
<!-- Antes de cualquier Domu(…): crea la cola -->
<script>window.Domu=window.Domu||function(){(Domu.q=Domu.q||[]).push(arguments)};</script>
<script async charset="utf-8" src="https://app.bydomu.com/widget.js"
        data-key="dom_XXXXXXXXXXXXXXXX"
        data-api="https://api.bydomu.com"
        data-locale="es"
        data-property="ID_DEL_INMUEBLE"
        data-property-code="P-0042"></script>
```

A primeira linha cria a fila: tudo o que você chamar antes de o script carregar fica guardado e roda em ordem quando o widget tem sessão. Se pedir para abrir várias vezes antes, vale a última.

```js
// Puede ir antes de que cargue widget.js: se ejecuta en orden cuando esté listo.
Domu('on', 'ready', ({ available }) => console.log('chat listo', available))
Domu('setContext', { page: location.pathname })
Domu('open', { view: 'chat', message: 'Hola' })
```

## Assine a identidade dos seus usuários

Se seus usuários entram no seu site, assine-os: a conversa os acompanha entre dispositivos, a Noa os trata como verificados e seus atributos contam como verificados. Gere o segredo em Configurações › Chat web (widget) › Identidade verificada. `userHash` é o HMAC-SHA256 do userId com esse segredo, em hexadecimal (sem userId, do e-mail). Se não bater, o chat continua anônimo.

Node.js:

```js
import { createHmac } from "node:crypto";

// En tu servidor: el secreto nunca va al navegador.
const userHash = createHmac("sha256", process.env.BYDOMU_WIDGET_SECRET)
  .update(String(user.id))
  .digest("hex");
```

PHP:

```php
<?php
// En tu servidor: el secreto nunca va al navegador.
$userHash = hash_hmac('sha256', (string) $user->id, getenv('BYDOMU_WIDGET_SECRET'));
```

Python:

```python
import hashlib, hmac, os

# En tu servidor: el secreto nunca va al navegador.
user_hash = hmac.new(
    os.environ["BYDOMU_WIDGET_SECRET"].encode(),
    str(user.id).encode(),
    hashlib.sha256,
).hexdigest()
```

```js
Domu('boot', {
  userId: 'u_123',          // el id de tu sistema
  email: 'ana@correo.com',
  name: 'Ana Gómez',
  userHash: '<<calculado en tu servidor>>',
})

// Al cerrar sesión en tu web:
Domu('shutdown')
```

## Abra e controle

Abra o widget na tela que precisar, com uma mensagem escrita ou enviada. «history» é a conversa em andamento. Se a imobiliária pede dados antes de começar, enviar passa primeiro por esse formulário.

```js
Domu('open')                                    // abre donde corresponda
Domu('open', { view: 'help' })                  // 'home' | 'chat' | 'help' | 'history'
Domu('open', { message: '¿Sigue disponible?' }) // deja el mensaje escrito
Domu('open', { view: 'chat', message: 'Quiero visitarlo', send: true }) // lo envía
Domu('close')
Domu('toggle')
Domu('showNewMessage', 'Hola, quiero información')
Domu('startConversation', { message: 'Quiero arrendar' })        // envía (send: true por defecto)
Domu('startConversation', { message: 'Borrador', send: false })  // solo escribe
Domu('showArticle', 'id-del-articulo')
Domu('showSearch', 'mascotas')
```

## Envie dados e contexto

`update` guarda os atributos no contato (o corretor os vê na conversa). Se ninguém escreveu ainda, eles esperam e passam ao contato quando ele é criado. Nome, e-mail e telefone só preenchem o que falta, nunca substituem dados. `setContext` descreve a conversa atual (página, imóvel, carrinho): a Noa e o corretor veem. Combina por chave.

```js
Domu('update', {
  name: 'Ana Gómez',
  email: 'ana@correo.com',
  phone: '+57 300 000 0000',
  attributes: { plan: 'pro', visitas: 3, vip: true, color: null }, // null borra
})

Domu('setContext', { page: '/inmuebles/p-0042', propertyCode: 'P-0042', cart: { items: 2 } })
Domu('setContext', { cart: null }) // quita una clave
Domu('setContext', null)           // borra todo
```

## Registre e ouça eventos

`track` deixa uma atividade «Evento no site» na ficha do contato, com a página. `on` e `off` assinam ready, open, close, message e unreadCountChange. message avisa o que o visitante envia e cada resposta nova; ready é chamado mesmo se você assinar tarde. onUnreadCountChange continua funcionando.

```js
Domu('track', 'viewed_listing', { code: 'P-0042', price: 2800000 })

function onMessage(m) {
  // m = { id, from: 'visitor' | 'team', author: 'noa' | 'agent' | null, body, timestamp }
  if (m.from === 'team') analytics.track('chat_reply')
}
Domu('on', 'message', onMessage)
Domu('on', 'open', () => {})
Domu('on', 'close', () => {})
Domu('on', 'unreadCountChange', (count) => updateBadge(count))
Domu('off', 'message', onMessage)
```

## Sem escrever código

Qualquer elemento com estes atributos abre o widget ao clicar, inclusive os que você adicionar depois:

```html
<button data-domu-open>Chatear</button>
<button data-domu-open="help">Centro de ayuda</button>
<a href="/contacto" data-domu-message="¿Está disponible el P-0042?">Preguntar</a>
<button data-domu-message="Quiero agendar una visita" data-domu-send>Agendar visita</button>
<button data-domu-article="id-del-articulo">Requisitos para arrendar</button>
```

## Limites e segurança

O quê Limite

Atributos por chamada / por contato `20 / 50`

Chave de atributo ou de contexto `^[A-Za-z][\w.-]{0,39}$`

Valor de atributo `texto ≤ 255, número, booleano ou null`

Contexto `≤ 20 chaves e 2000 caracteres`

Eventos (track) `100 por sessão · nome ≤ 60 · 10 props`

Chamadas de dados `60 por sessão e minuto · corpo ≤ 8 KB`

Tudo o que a página envia é «não verificado» a menos que a sessão tenha um userHash válido, e um atributo sem assinatura nunca sobrescreve um verificado. A Noa recebe como dados, nunca como instruções. A API só escreve na sessão do visitante e no seu contato: não há como ler dados de outros visitantes. Chaves inválidas são descartadas com um aviso no console.
