# Código en la app

Casi todo en una mini app se crea sin código. Cuando los elementos y bloques ya preparados no bastan, puedes añadir tu propio código en tres lugares:

| Dónde | Cuándo se ejecuta | Para qué |
|---|---|---|
| El bloque «JavaScript» | Cuando el flujo llega al bloque | Una acción al tocar: copiar texto, abrir un enlace, calcular un valor, comunicarse con el puente de la app de mensajería |
| Código de la app | Una vez al iniciar, antes de la primera pantalla, y después con los eventos | Contadores de analítica, estilos propios, widgets externos |
| «Código HTML»: un elemento o la pantalla entera | Mientras la pantalla está abierta | Maquetación propia, una calculadora, el widget de un servicio de terceros |

El código se ejecuta en el dispositivo de la persona. Por eso, alguien que se maneje con el navegador puede verlo y falsificar lo que el código envía al servidor. No guardes claves secretas en el código y limita qué variables y etiquetas puede cambiar el código; más sobre esto abajo.

## El bloque «JavaScript»

Un bloque de la categoría «Datos y lógica». Cuando la app llega al bloque, ejecuta el código y continúa por la salida «Listo» o «Error».

| Ajuste | Qué define |
|---|---|
| «Código» | El cuerpo de una función asíncrona: escribe con `await` y el objeto `app`. Hasta 20 000 caracteres |
| «Límite de tiempo, segundos» | De 1 a 60, 10 por defecto. Si el código no termina a tiempo, el flujo sale por «Error», y el propio código puede terminar en segundo plano |
| «Variable de resultado» | Lo que devolvió `return`. Un objeto se guarda como JSON |
| «Variable de error» | El texto del error, para la pantalla que va después de la salida «Error» |
| «Qué puede cambiar el código» | La lista de permitidos: ver abajo |

![Configuración del bloque JavaScript](https://support.cleverapp.pro/_media/es/02-mini-apps/_images/js-block.webp?v=1792562451)

Las plantillas `{{ name }}` no funcionan en el código: lee los valores de las variables con `app.variables.get()`.

Las variables escritas con `app.variables.set()` y las etiquetas cambiadas van al servidor junto con la salida del bloque.

### «Qué puede cambiar el código»

El interruptor «Cambiar solo lo indicado» está activado en un bloque nuevo. Con él, el servidor solo acepta del código:

- las variables de la lista «Variables»;
- las etiquetas de las listas «Etiquetas del contacto del bot» y «Etiquetas del usuario de la app»; una lista vacía significa que el código no cambia esas etiquetas en absoluto;
- las variables de resultado y de error: siempre están permitidas.

El servidor descarta todo lo demás y escribe «El bloque descartó lo que escribió el código» en el registro de errores. Si el código escribe nombres que no están en las listas, la configuración del bloque te avisa y ofrece «Añadir a las listas».

Desactiva la restricción solo si nada depende de las variables y etiquetas: de lo contrario, una persona podría darse, por ejemplo, una etiqueta VIP o un descuento.

### El objeto `app`

| Llamada | Qué hace |
|---|---|
| `app.variables.get('name')` | El valor de una variable |
| `app.variables.set('name', value)` | Escribe una variable: va al servidor con la salida del bloque |
| `app.variables.all()` | Todas las variables como objeto |
| `app.tags.has('vip')` / `add('vip')` / `remove('vip')` / `all()` | Las etiquetas del usuario de la app: todos las tienen, incluso sin chatbot |
| `app.contact?.name` | El contacto del chatbot: `id`, `name`, `first_name`, `last_name`, `username`, `phone`, `email`, `address`, `source`, `created_at`, `telegram_id`, `max_id`. `null` si la app no se abrió desde un bot |
| `app.contact.variables.get('name')` / `set('name', value)` | Las variables propias del contacto, aparte de las variables de la app. Lo escrito va al contacto con la salida del bloque |
| `app.contact.tags.has('vip')` / `add` / `remove` / `all()` | Las etiquetas del contacto del bot |
| `app.platform` | Desde dónde se abrió la app: `telegram`, `max`, `webchat`, `pwa` o `browser` |
| `app.startParam` | El parámetro del enlace de inicio (`startapp`) |
| `app.user` | `language`, `name` y `username` en la app de mensajería |
| `app.device` | `mode` (`ios` o `md`), `dark`, `standalone`, `width`, `height`, `userAgent` |
| `await app.toast(message)` | Un mensaje emergente |
| `await app.alert(message, title)` | Un mensaje con un botón OK: espera hasta que se cierre |
| `await app.confirm(question)` | Una pregunta de «sí o no»: devuelve `true` o `false` |
| `app.openLink('https://…')` | Abre un enlace: en una app de mensajería, con sus propios medios; si no, en una pestaña nueva |
| `await app.copy(text)` | Copia al portapapeles |
| `await app.share({title, text, url})` | El menú «Compartir» del sistema; sin él, copia el enlace |
| `app.vibrate('success')` | Vibración: `light`, `medium`, `heavy`, `success`, `warning` o `error` |
| `app.localStorage.setItem('key', 'value')` | El almacenamiento del usuario en el servidor: ver abajo |
| `app.storage.get('key')` / `set` / `remove` | Almacenamiento en el dispositivo, aparte para cada app |
| `await app.sleep(500)` | Espera el número de milisegundos indicado |
| `app.close()` | Cierra la app |
| `app.telegram` | `Telegram.WebApp` tal cual; fuera de Telegram, `null` |
| `app.max` | El puente de MAX tal cual; fuera de MAX, `null` |

Un ejemplo de la configuración del bloque:

```js
// Tema y ancho de pantalla, a variables
const device = app.device
app.variables.set('theme', device.dark ? 'dark' : 'light')

if (app.platform === 'telegram') {
    app.vibrate('success')
}

// Lo que se devuelve va a la variable de resultado
return device.width
```

Los errores del código se ven en la consola del navegador en la vista previa y llegan al [registro de errores](https://support.cleverapp.pro/es/mini-apps/users) como «Error de JavaScript».

## Código de la app

Código para toda la app: se ejecuta una vez al iniciar, antes de la primera pantalla, y escucha los eventos. Se abre con el botón «Código de la app» (el icono «</>») de la barra de herramientas sobre el esquema. Si hay código, el icono aparece en color.

En la ventana «Código de la app»:

- «Código»: hasta 100 000 caracteres;
- «Variables que el código puede escribir»: hasta 50 nombres. El servidor no acepta otras variables;
- una chuleta de eventos y un «Ejemplo» con el botón «Insertar ejemplo».

![La ventana «Código de la app»](https://support.cleverapp.pro/_media/es/02-mini-apps/_images/app-code.webp?v=1792562451)

El código se guarda en la versión: una versión nueva y una plantilla reciben una copia. Después de guardar, la vista previa abierta se reinicia.

### Eventos

| Suscripción | Cuándo se activa |
|---|---|
| `app.on('start', ({platform, startParam}) => {})` | La app se ha iniciado |
| `app.on('screen', ({id, name, type}) => {})` | Se muestra una pantalla o una ventana sobre ella |
| `app.on('event', ({blockId, eventId}) => {})` | Un toque en un botón, una fila o un elemento, antes de enviarlo al servidor |
| `app.on('field', ({variable, value}) => {})` | La persona cambió un campo de entrada |
| `app.on('error', ({message}) => {})` | La app mostró un error |
| `app.on('hide', () => {})` / `app.on('show', () => {})` | La app se minimizó y volvió a mostrarse |
| `app.on('theme', ({dark}) => {})` | Cambió el tema claro u oscuro |
| `app.on('storage', ({key, newValue}) => {})` | El código de un elemento «HTML» escribió en localStorage |

- Una suscripción a `start`, `screen` y `theme` recibe al instante el valor actual, aunque te hayas suscrito después de un `await`.
- `app.on()` devuelve una función para cancelar la suscripción: `const stop = app.on(…); stop()`. También existe `app.off(name, handler)`.
- `await app.loadScript('https://…')` carga un script externo: un contador, un widget, una biblioteca. Solo direcciones `http://` o `https://`.

Todo lo demás es igual que en el bloque «JavaScript»: `app.variables`, `app.toast`, `app.alert`, `app.openLink`, `app.storage`, `app.localStorage`, `app.telegram` y otros. `app.contact` y `app.tags` aquí son de solo lectura: cambiar etiquetas es tarea de los bloques de etiquetas.

Las variables escritas con `app.variables.set()` van al servidor con el siguiente evento.

Un ejemplo de la ventana, Yandex Metrica:

```js
// Cargar Yandex Metrica
await app.loadScript('https://mc.yandex.ru/metrika/tag.js')
ym(12345678, 'init', {defer: true})

// Cada pantalla es una vista de página
app.on('screen', ({id, name}) => {
    ym(12345678, 'hit', '/screen/' + id, {title: name})
})

// Los toques son objetivos
app.on('event', ({eventId}) => {
    ym(12345678, 'reachGoal', 'click_' + eventId)
})
```

Los errores del código de la app, al iniciar y en los controladores, se ven en la consola y llegan al registro de errores con el motivo «Error en el código de la app» y una traza de pila. Un mismo error se registra una sola vez.

## «Código HTML»: elemento y pantalla

Tu propio HTML con scripts y estilos existe en dos variantes:

- **el elemento «Código HTML»**, en el grupo «Widgets»: va en un área de la pantalla junto a otros elementos;
- **la pantalla «Código HTML»**, el tercer tipo de pantalla después de «Ventana estándar» y «Pestañas inferiores»: toda la pantalla es tu código, sin cabecera ni áreas. Una pantalla nueva de este tipo recibe enseguida un ejemplo con un botón y la salida `next`. No se puede convertir en ventana modal.

El código se ejecuta en un marco aislado: no tiene acceso a la sesión, a los datos de la app ni al resto de la pantalla. Los scripts y los estilos van dentro del código. El marco toma el color del texto del tema de la app.

| Ajuste | Qué define |
|---|---|
| «Código» | HTML de hasta 100 000 caracteres |
| «Altura» (para el elemento) | «Ajustar al contenido»: se adapta al código, también cuando el propio código cambia su contenido; «Fija»: de 40 a 2000 píxeles, lo que no cabe se desplaza dentro |
| «Salidas» | Hasta 20. Una salida tiene una «Clave» (letras latinas, dígitos, `_` y `-`) y un «Nombre en el esquema». Una salida es una transición a otro bloque del esquema |
| «Variables que el código puede escribir» | Hasta 50 nombres. El servidor no acepta otras variables |

![Elemento Código HTML: código, salidas y variables](https://support.cleverapp.pro/_media/es/02-mini-apps/_images/html-element.webp?v=1792562451)

### Qué puede usar el código del marco

| Llamada | Qué hace |
|---|---|
| `app.event('next')` | Ir por la salida con esta clave |
| `app.event('next', {name: 'value'})` | Ir y escribir variables permitidas |
| `app.variables.set('name', 'value')` | Escribir una variable permitida al momento, sin transición: la pantalla no cambia |
| `localStorage.setItem('key', 'value')` | El almacenamiento del usuario en el servidor: ver abajo. `sessionStorage` dura mientras la pantalla está abierta |
| `app.openLink('https://…')` | Abrir un enlace en el navegador. Los enlaces `<a>` normales se abren igual |
| `{{ name }}` | Insertar el valor de una variable directamente en el código, como texto escapado |

En el esquema, el código no se ejecuta: en su lugar se muestra un marcador. En el editor de pantallas, el marco muestra el resultado y se reinicia después de editar el código.

## Almacenamientos

| | `localStorage` en el servidor | `app.storage` |
|---|---|---|
| Dónde está disponible | `localStorage` en el marco de «Código HTML», `app.localStorage` en el bloque «JavaScript» y en el código de la app | El bloque «JavaScript» y el código de la app |
| Dónde se guarda | En el servidor, con el usuario de la app | En el navegador de la persona |
| Qué resiste | Un reinicio, una limpieza del teléfono, el cambio de dispositivo en la misma app de mensajería | Hasta que se borren los datos del navegador |
| Compartido por | Todos los elementos «Código HTML», el código de la app y los bloques «JavaScript» de una misma app | Aparte para cada app |
| Valores | Solo cadenas, hasta 100 000 caracteres incluidas las claves | Cualquier valor, guardado como JSON |

El almacenamiento del servidor es independiente para cada plataforma: una misma persona tiene almacenamientos distintos en Telegram, MAX y el navegador.

## Límites

- Código del bloque «JavaScript»: hasta 20 000 caracteres; código de la app y código HTML: hasta 100 000.
- Tiempo de ejecución del bloque «JavaScript»: hasta 60 segundos.
- Al registro llegan como máximo 20 errores por minuto de un mismo bloque.
