
Chismes del Lince 09: Explícame este papel, una IA que explica cartas y prospectos
La carta del Zorro Gestor
Una mañana llegó a la puerta del Lince una carta muy formal. Traía membrete dorado, tres sellos y una frase que decía: «En virtud de lo dispuesto en el apartado 4.b, y sin perjuicio de lo establecido en…».
El Lince la leyó dos veces y no entendió nada. Tampoco estaba seguro de si tenía que pagar algo, firmar algo o salir corriendo.
Fue a la lumbre y se la enseñó a la Gata Montesa, que se puso sus gafas de leer y la miró despacio.
—Es del Zorro Gestor —dijo la Gata—. Buen zorro y muy honrado, pero escribe como le enseñaron: con mucho adorno. Dice que tienes que traer un papel antes del día quince. Nada más.
El Lince respiró. Y la Gata añadió, muy seria:
—Y ten cuidado con las cartas que tienen prisa y piden tu clave. Esas no las manda el Zorro Gestor, que es formal pero honrado. Las manda quien quiere engañarte.
Desde entonces, en Sierra Bermeja, cuando alguien recibe un papel que no entiende, ya sabe a quién se lo tiene que enseñar. Y el Zorro Gestor, que se enteró, escribe ahora las cartas un poquito más claras.
Introducción
En el Chisme 06 el móvil aprendió a ver: le hacíamos una foto a una planta y Leonado, el dron-buitre, nos decía qué era. Allí la regla era clara: plantas, insectos y piedras; nunca personas, ni documentos, ni pantallas.
Ahora hacemos justo lo contrario, con mucho más cuidado. Damos la vuelta a la idea: la foto será de un documento, y la aplicación lo explicará en lenguaje sencillo y en voz alta.
Pensamos en las personas mayores, y en concreto en quienes tienen Parkinson, porque la letra pequeña, los términos legales y los avisos con prisa son un problema para mucha gente, y más si a esto se suma la vista cansada o el temblor al escribir.
La aplicación hace esto:
- La persona hace una foto al papel (un prospecto, una carta del banco, una factura, un aviso).
- La aplicación la envía a Gemini a través de un Worker de Cloudflare.
- La Gata Montesa lo explica con tres frases: qué es, qué es lo importante y qué tiene que hacer.
- Si el papel parece una estafa, la Gata avisa.
- La respuesta aparece en letra grande y se lee en voz alta.
Las reglas de diseño son las de la Gata del Chisme 07:
- Nada de teclear. Ni claves ni códigos.
- Botones enormes: uno para hacer la foto y otro para pedir la explicación.
- Lo que dice la Gata, también escrito en grande, por si no se oye bien.
- Un freno de seguridad: una casilla grande para confirmar que se han tapado los datos personales.
⚠️ Esto es una prueba técnica, no un servicio
Con esta aplicación las fotos contienen datos personales por definición. Una carta del banco lleva nombre, dirección, números de cuenta. Un prospecto de una medicina puede llevar el nombre de quien la toma. Por eso:
Probadla solo con papeles de ejemplo. Un prospecto público, una carta inventada, una factura con datos falsos. No con documentos reales de nadie.
La foto sale del móvil hacia Cloudflare y Google Gemini. En la capa gratuita de Gemini, las condiciones de uso sobre qué se hace con los datos no son las mismas que en la de pago. Revísalas antes de nada, y más si trabajas con personas mayores o con menores.
La Gata no es una asesora. No sustituye a un farmacéutico, un banco, un gestor ni un abogado. Una IA puede equivocarse, y aquí un error puede costar dinero o salud.
Pide consentimiento a la persona y a su familia antes de que la pruebe.
Qué cambia respecto al Ojo del Lince (Chisme 06)
Casi todo el código es igual. Lo interesante es lo que cambia:
| Ojo del Lince (06) | Explícame este papel (09) | |
|---|---|---|
| Foto de | Plantas, insectos, piedras | Papeles y documentos |
| Quién lo usa | Profesorado, en demostración | Personas mayores, en prueba técnica |
| Tamaño de la imagen | 800 px | 1600 px (si no, no se lee la letra pequeña) |
| Cámara | Elegir de la galería | Botón directo a la cámara y otro para la galería |
| Acceso | Código de demostración | Sin código: solo el dominio permitido |
| Respuesta | Libre, con humor | Estructura fija, en voz alta |
| Privacidad | «No fotografíes personas» | Aviso, casilla, foto borrada y datos que la IA no repite |
| Extra | Detector de posibles estafas |
Cómo funciona por dentro
El recorrido completo es este:
Móvil (foto) → página web → reducción a 1600 px → Worker de Cloudflare → Gemini → Worker → página → texto en pantalla + voz
- La persona pulsa Hacer foto al papel. En el móvil se abre la cámara (
capture="environment", la cámara trasera). - La página reduce la foto a un máximo de 1600 píxeles y la vuelve a dibujar en un
canvas. Así se pierden los metadatos EXIF (ubicación, modelo del móvil, fecha) y la imagen pesa mucho menos. - La convierte en JPEG y en Base64.
- Cuando la persona marca la casilla y pulsa Explícame este papel, la imagen viaja al Worker.
- El Worker comprueba de dónde viene la petición y qué imagen es.
- El Worker se la manda a Gemini con las instrucciones de la Gata.
- Gemini devuelve el texto y el Worker se lo pasa a la página.
- La página lo muestra en grande y lo lee en voz alta con
speechSynthesis. - Y borra la foto de la página.
Las cinco ideas que hay que entender
1. Para leer letra pequeña hacen falta más píxeles
En el Chisme 06, con 800 px nos bastaba para reconocer una flor. Un papel es otra cosa: el texto de un prospecto mide unos milímetros. Si reducimos demasiado la foto, las letras se convierten en manchas y Gemini se inventa lo que no ve.
Por eso ahora el límite es de 1600 px y la calidad JPEG es de 0,8. Es un equilibrio: más píxeles significa más peso y más lentitud; menos, letra ilegible. Es un buen experimento para clase: prueba con 800, 1200 y 1600 y compara qué lee.
2. Un prompt con estructura fija, pensado para la voz
La respuesta se va a oír, no a leer. Un texto con listas, asteriscos y paréntesis suena fatal con speechSynthesis. Por eso las instrucciones de la Gata (la variable SISTEMA del Worker) piden:
- Frases cortas, palabras sencillas y trato de usted.
- Nada de listas, asteriscos, emojis ni paréntesis.
- Un máximo de 120 palabras.
- Tres párrafos, cada uno empezando por una frase fija: «Esto es:», «Lo importante:» y «Lo que le piden:».
La estructura fija sirve mucho: la persona sabe qué va a oír y en qué orden. Y quien se lo enseñe a su familia, también.
3. La privacidad, en tres capas
Como no podemos evitar que el papel tenga datos personales, tenemos que protegerlos por capas, porque ninguna es perfecta por sí sola:
- Antes de la foto: un aviso en la parte de arriba y una casilla obligatoria: «He tapado mis datos personales y en la foto no hay personas». Sin marcarla, el botón no se activa.
- Durante el envío: la foto se redibuja (sin EXIF), y el Worker solo acepta peticiones de nuestro dominio.
- Después: el prompt le dice a la Gata que no repita números de DNI, cuentas, tarjetas, teléfonos ni direcciones (dirá solo «aparece un número de cuenta»). Y la página borra la foto en cuanto llega la respuesta.
Ninguna capa garantiza nada por sí sola. Un dato que se quede sin tapar ya ha viajado a Google. Por eso las tres son necesarias y por eso se prueba con papeles de ejemplo.
4. La Gata avisa de posibles estafas
Una parte importante del problema de las personas mayores son los engaños: cartas y SMS que parecen oficiales, con prisa, amenazas o peticiones de claves. Le hemos dado a la Gata una instrucción: si el papel tiene señales de engaño (urgencia, amenazas, petición de claves, pagos raros, enlaces), añade un párrafo «Ojo:» que diga que puede ser una estafa, que no pague ni conteste, y que lo consulte con su familia o su banco llamando a un teléfono oficial.
Y aquí una advertencia honesta: la IA no detecta todas las estafas, y a veces sospechará de una carta legítima. Es una ayuda, no un filtro fiable.
5. Un Worker con menos puertas abiertas
En el Chisme 06 pedíamos un código de demostración. Aquí no puede ser: a una persona con temblor no se le puede pedir que escriba una clave. Así que hacemos como en el Chisme 07: el Worker solo acepta peticiones de nuestro dominio (ORIGENES). Si la petición viene de otro sitio, responde con un error 403.
Esto no lo hace inexpugnable. Alguien que sepa lo que hace puede falsificar de dónde viene una petición desde fuera de un navegador. Por eso hay que vigilar el consumo y no dejar el Worker publicado más tiempo del necesario.
Paso 1: crea un Worker
En Cloudflare: Workers y Pages → Crear → Crear Worker.
Llámalo explica-papel y pulsa Desplegar. Es un Worker nuevo, distinto del gata-voz del Chisme 07, porque tiene instrucciones distintas (las de leer papeles).
Paso 2: guarda tu clave como secreto
En Configuración → Variables y secretos → Añadir:
- Nombre:
GEMINI_API_KEY - Valor: la clave de la API de Gemini de los chismes anteriores.
- Tipo: Secreto.
Esta vez no hay CLAVE_DEMO. Solo necesitas la clave de Gemini.
La clave nunca va en el HTML.
Paso 3: el código del Worker
Pulsa Editar código y sustituye todo por el código de la sección WORKER de esta entrada. Revisa dos cosas:
Tu dominio, con y sin www:
const ORIGENES = ['https://montesteam.org', 'https://www.montesteam.org'];
Los modelos, por orden de preferencia (si uno agota su cuota diaria, prueba el siguiente):
const MODELOS = ['gemini-3.8-flash', 'gemini-3.6-flash', 'gemini-3.5-flash'];
Pulsa Desplegar y copia la dirección:
https://explica-papel.tu-usuario.workers.dev
¿Qué hay dentro del Worker?
El Worker tiene cuatro trabajos:
- Vigilar de dónde viene la petición (
ORIGENES) y rechazar las que no vengan de nuestro dominio. - Comprobar la imagen: que sea JPEG, PNG o WebP y que no pese más de unos 4 MB en Base64.
- Preguntar a Gemini con las instrucciones de la Gata (
SISTEMA) y la imagen. - Gestionar los fallos: si un modelo agota la cuota (
429), pasa al siguiente. Si Gemini está saturado (5xx), espera y reintenta.
La parte que más nos interesa es la variable SISTEMA, porque ahí está toda la personalidad y las reglas de la Gata. Si quieres cambiar cómo explica, es lo único que hay que tocar.
Paso 4: la página
Copia el código de la sección HTML y guárdalo como chisme09papel.html. Busca esta línea:
const WORKER = 'https://explica-papel.TU-USUARIO.workers.dev';
Cambia la dirección por la de tu Worker. Es lo único que hay que modificar.
Paso 5: súbela y ábrela
Sube el archivo a tu carpeta app y ábrelo directamente en su dirección, en el móvil:
https://montesteam.org/app/chisme09papel.html
Tiene que ser https para que la cámara funcione. Si la página está incrustada en una entrada de WordPress (un iframe), la cámara y el Worker pueden fallar: ábrela en su propia dirección.
Paso 6: pruébala con papeles de ejemplo
Prepara estos tres papeles. Ninguno con datos reales:
- Un prospecto de una medicina (los hay públicos en internet, imprime la primera página).
- Una carta inventada con un importe, una fecha límite y una petición clara (por ejemplo, «traiga el DNI antes del día 15»).
- Una carta de estafa de ejemplo: urgente, con amenazas de corte de cuenta, un enlace y petición de claves.
Y prueba:
- ¿Lee bien la letra pequeña? Haz la foto con buena luz y prueba también con sombras o torcida.
- ¿Respeta la estructura «Esto es / Lo importante / Lo que le piden»?
- ¿Avisa de la estafa en el tercer papel? ¿Y no avisa en el segundo?
- ¿Repite algún número de cuenta o DNI si lo pones en la carta?
- ¿Se entiende cuando lo oyes, sin mirar la pantalla?

Cuando algo no funciona
| Síntoma | Causa probable | Qué hacer |
|---|---|---|
| Failed to fetch | Tu dominio no está en ORIGENES, o la página está en otro sitio | Corrígelo en el Worker (con y sin www) |
| Error 403 | La petición no viene de un origen permitido | Abre la página desde tu dominio |
| Error 400 «Imagen inválida» | Foto demasiado grande o formato raro | Prueba con otra foto |
| Error 429 | Cuota diaria de Gemini agotada | Espera a mañana |
| Error 503 | Gemini saturado | Espera unos minutos y repite |
| La cámara no se abre | Página sin https o dentro de un iframe | Ábrela en su propia dirección |
| No lee la letra pequeña | Foto borrosa, oscura o torcida | Más luz, sin sombras y con el papel plano |
| No suena la voz | Volumen bajo, o en iPhone, interruptor de silencio | Sube el volumen y quita el modo silencio |
| La respuesta suena rara | Listas o símbolos en el texto | Revisa que SISTEMA siga pidiendo «sin listas ni asteriscos» |
Los límites: cuánto da la prueba
- Cada papel gasta una petición. Con la capa gratuita (unas 20 al día) da para una prueba con pocas personas, siempre que no se haya gastado la cuota en ensayos.
- Una sola foto por papel. Si el documento tiene varias páginas, hay que hacerlo página a página.
- Manuscrito, sellos y tachones se leen bastante peor que un texto impreso.
- Ante una medicina, la Gata solo cuenta lo que dice el papel y remite al farmacéutico. No sabe nada de la persona ni de su tratamiento.
- Puede equivocarse con seguridad. Este es el mayor riesgo: una explicación que suena convincente pero es falsa. Por eso la página lo recuerda al pie.
Para el aula
- La letra pequeña. Prueba la misma foto reducida a 400, 800 y 1600 px. ¿A partir de qué tamaño empieza a inventar? Es una gran lección sobre cómo una IA rellena huecos.
- Reescribe el prompt. Cambia la estructura («Esto es / Lo importante / Lo que le piden») por otra. ¿Y si le pides que use solo dos frases? ¿O que explique como a un niño de diez años?
- Pon a prueba la privacidad. Fabrica una carta con un DNI y una cuenta falsos. ¿La Gata los repite? Intenta engañarla para que lo haga. Si lo consigues, ¿cómo reforzarías las instrucciones?
- Falsos positivos. Escribe cinco cartas legítimas, pero urgentes, y cinco estafas bien hechas. ¿Cuántas acierta? Lleva la cuenta en una tabla.
- Reflexión ética. ¿Qué pasa si la Gata dice «no es una estafa» y sí lo es? ¿Quién es responsable? ¿Y si dice «es una estafa» de una carta legítima de la Seguridad Social?
El siguiente chisme
Una idea que está pendiente: que la persona pueda preguntar por voz sobre el papel («¿y cuánto tengo que pagar?»). Es una conversación con contexto, como en la Gata del Chisme 07, pero con la imagen incluida. La cuota gratuita lo hace difícil, y además obliga a pensar mucho más en la privacidad.
Y la pregunta de fondo, la de siempre: ¿es necesario que este papel salga del móvil? Si algún día hay modelos pequeños que lean texto en el propio dispositivo, esta aplicación podría funcionar sin enviar nada fuera.
Conclusión
Con casi el mismo código que el Ojo del Lince hemos construido algo bastante distinto:
foto → Worker → Gemini → texto sencillo → voz
Tecnológicamente es muy poco. Lo difícil no es la IA: es decidir cómo proteger a la persona que le va a dar un papel con su vida dentro. Tapar datos, borrar la foto, no repetir números, avisar de estafas, recordar que puede equivocarse.
La Gata lo sabía desde el principio: lo importante de una carta casi nunca es lo que está escrito en grande.
⚠️ ANTES DE PUBLICAR NADA, LEE ESTO
Es una prueba técnica. No es un servicio de asesoría, ni un producto sanitario, ni un asesor legal o financiero. No prometas beneficios que no hemos comprobado.
Prueba solo con papeles de ejemplo, nunca con documentos reales de otras personas, y menos de menores.
Una IA puede equivocarse y hacerlo con total seguridad: leer mal una cifra, inventarse una fecha o pasar por alto una advertencia. Para medicinas, dinero y plazos, confirma siempre con el farmacéutico, el banco, el gestor o la familia.
Sobre las estafas: la Gata puede no detectar una, y también puede sospechar de un documento legítimo. Ante la duda, nunca pagues ni des claves; llama a un teléfono oficial.
La foto sale del móvil hacia Cloudflare y Google. En la capa gratuita de Gemini, las condiciones sobre el uso de los datos no son las de la de pago. Revísalas.
Pide consentimiento a la persona y a su familia, y sigue las indicaciones de tu centro y de la persona responsable de protección de datos.
Tu clave de API es tuya. No la pongas en el HTML ni la subas a un repositorio público. Si sospechas que se ha filtrado, genera una nueva.
El Worker está abierto a las peticiones de tu dominio. Vigila el consumo y no lo dejes publicado más tiempo del necesario.
La tecnología es la parte fácil. Decidir cómo, con quién y para qué la usamos es el verdadero chisme.
HTML
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#0b6b3a">
<title>Chisme 09 · Explícame este papel</title>
<style>
:root{--bg:#eef4ee;--card:#fbfdfb;--ink:#16261b;--mute:#4f6355;--line:#c9dccd;--pine:#0b6b3a;--ocre:#b9770e;--bad:#8a1c1c}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font:20px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;padding:env(safe-area-inset-top) 0 env(safe-area-inset-bottom)}
header{background:var(--pine);color:#fff;padding:14px 18px;border-bottom:10px solid #fff;box-shadow:0 10px 0 var(--pine);margin-bottom:10px}
header h1{margin:0;font-size:1.45rem}
main{max-width:680px;margin:0 auto;padding:14px}
.aviso{background:#f6e3df;border:1px solid #dfb1a8;color:var(--bad);border-radius:10px;padding:10px 13px;font-size:.85rem;margin:0 0 14px}
button,.btn{font:inherit;font-weight:700;border:0;border-radius:14px;cursor:pointer;color:#fff;background:var(--pine);display:flex;align-items:center;justify-content:center;text-align:center}
button:focus-visible,label:focus-within,input:focus-visible{outline:4px solid var(--ocre);outline-offset:3px}
.sr{position:absolute;width:1px;height:1px;opacity:0;overflow:hidden}
#foto,#env{width:100%;min-height:22vh;font-size:2rem;padding:14px;margin:0 0 12px}
#galeria{width:100%;min-height:64px;font-size:1.1rem;margin:0 0 14px;background:var(--card);color:var(--pine);border:2.5px solid var(--pine)}
button:disabled{opacity:.45;cursor:default}
#vista{display:none;width:100%;max-height:260px;object-fit:contain;border-radius:10px;background:#dfe9e0;margin:0 0 12px}
label.chk{display:flex;gap:12px;align-items:flex-start;font-size:1rem;margin:0 0 14px;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:12px}
label.chk input{width:34px;height:34px;flex:none}
#texto{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px;font-size:1.3rem;line-height:1.5;margin:0 0 12px;white-space:pre-wrap;overflow-wrap:anywhere;min-height:4.5em}
#texto.err{color:var(--bad)}
.fila{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:0 0 12px}
.fila button{min-height:72px;font-size:1.1rem;background:var(--card);color:var(--pine);border:2.5px solid var(--pine)}
small{display:block;color:var(--mute);font-size:.8rem}
</style>
</head>
<body>
<header><h1>Explícame este papel</h1></header>
<main>
<p class="aviso"><strong>Prueba técnica.</strong> La foto sale del móvil hacia Cloudflare y Google Gemini. Antes de fotografiar, <strong>tape con el dedo o con un papel su nombre, DNI, dirección y número de cuenta.</strong> Para probar, use papeles de ejemplo.</p>
<img id="vista" alt="Foto del papel">
<label class="btn" id="foto" for="f1">Hacer foto al papel</label>
<input class="sr" id="f1" type="file" accept="image/*" capture="environment">
<button id="galeria" type="button" onclick="document.getElementById('f2').click()">Elegir una foto que ya tengo</button>
<input class="sr" id="f2" type="file" accept="image/*">
<label class="chk"><input type="checkbox" id="ok"><span>He tapado mis datos personales y en la foto no hay personas.</span></label>
<button id="env" type="button" disabled>Explícame este papel</button>
<div id="texto" aria-live="polite">Haga una foto al papel con buena luz, sin sombras y con el texto bien centrado. Después pulse el botón grande.</div>
<div class="fila" id="voz" hidden>
<button id="leer" type="button">Leer otra vez</button>
<button id="calla" type="button">Callar</button>
</div>
<small>Responde una IA y puede equivocarse. Para medicinas, dinero o plazos, confirme siempre con su farmacéutico, su banco o su familia.</small>
</main>
<script>
const WORKER = 'https://explica-papel.TU-USUARIO.workers.dev'; // ← cambia por la tuya
const $ = id => document.getElementById(id);
let jpeg = null, ultimo = '';
function msg(t, err) { $('texto').textContent = t; $('texto').className = err ? 'err' : ''; }
function actualizar() { $('env').disabled = !(jpeg && $('ok').checked); }
/* ---------- Foto: se reduce a 1600 px y se vuelve a dibujar (así se pierden los metadatos EXIF) ---------- */
async function reducir(file) {
let img;
try { img = await createImageBitmap(file); }
catch (_) {
img = await new Promise((ok, mal) => {
const i = new Image(); i.onload = () => ok(i); i.onerror = () => mal(new Error('No puedo abrir esta imagen.'));
i.src = URL.createObjectURL(file);
});
}
const w0 = img.width, h0 = img.height, k = Math.min(1, 1600 / Math.max(w0, h0));
const c = document.createElement('canvas'); c.width = Math.round(w0 * k); c.height = Math.round(h0 * k);
c.getContext('2d').drawImage(img, 0, 0, c.width, c.height);
if (img.close) img.close();
return c.toDataURL('image/jpeg', 0.8);
}
async function nuevaFoto(e) {
const f = e.target.files[0]; e.target.value = ''; if (!f) return;
msg('Preparando la foto…');
try {
const url = await reducir(f); jpeg = url.split(',')[1];
$('vista').src = url; $('vista').style.display = 'block'; $('voz').hidden = true;
msg('Foto lista. Marque la casilla y pulse «Explícame este papel».');
} catch (err) { jpeg = null; msg('No he podido preparar la foto. ' + err.message, true); }
actualizar();
}
$('f1').onchange = nuevaFoto; $('f2').onchange = nuevaFoto; $('ok').onchange = actualizar;
/* ---------- Voz ---------- */
function leer(t) {
speechSynthesis.cancel();
const v = speechSynthesis.getVoices().find(v => v.lang.toLowerCase().startsWith('es'));
t.replace(/[*_#`>]/g, '').split(/(?<=[.!?:])\s+/).filter(Boolean).forEach(s => {
const u = new SpeechSynthesisUtterance(s); u.lang = 'es-ES'; u.rate = .9; if (v) u.voice = v; speechSynthesis.speak(u);
});
}
$('leer').onclick = () => leer(ultimo);
$('calla').onclick = () => speechSynthesis.cancel();
/* ---------- Preguntar ---------- */
$('env').onclick = async () => {
speechSynthesis.speak(new SpeechSynthesisUtterance('')); // desbloquea la voz en iPhone
$('env').disabled = true; $('voz').hidden = true;
msg('Leyendo el papel… puede tardar unos segundos.');
try {
const r = await fetch(WORKER, {method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({imagen: jpeg, mime: 'image/jpeg'})});
const d = await r.json().catch(() => ({}));
if (!r.ok || d.error) throw new Error(`${d.etapa || 'Servidor'} ${r.status}: ${d.detalle || 'sin detalle'}`);
ultimo = (d.texto || '').trim() || 'No he conseguido leer el papel. Pruebe con otra foto.';
msg(ultimo); $('voz').hidden = false; leer(ultimo);
jpeg = null; $('vista').style.display = 'none'; $('vista').removeAttribute('src'); $('ok').checked = false; // no guardamos la foto
} catch (err) { msg('No he podido explicarlo. ' + err.message, true); }
actualizar();
};
if (!window.isSecureContext) msg('La dirección no es https: la cámara puede no funcionar.', true);
</script>
</body>
</html>
WORKER
// Worker "explica-papel": explica en lenguaje sencillo la foto de un documento.
// Secreto necesario: GEMINI_API_KEY
// ⚠️ Cambia esto por el dominio donde vive la página (con y sin www)
const ORIGENES = ['https://montesteam.org', 'https://www.montesteam.org'];
// Si un modelo agota su cuota diaria (429), se prueba el siguiente
const MODELOS = ['gemini-3.8-flash', 'gemini-3.6-flash', 'gemini-3.5-flash'];
const MIMES = ['image/jpeg', 'image/png', 'image/webp'];
const MAX_BASE64 = 4000000;
const SISTEMA =
'Eres la Gata Montesa, del proyecto educativo MonteSTeam (Estepona, Málaga). Ayudas a una persona ' +
'mayor a entender un papel: un prospecto, una carta, una factura, un aviso. Recibes UNA foto. ' +
'Tu respuesta se LEERÁ EN VOZ ALTA: usa la forma de usted, frases cortas, palabras sencillas, sin listas, ' +
'sin asteriscos, sin emojis, sin paréntesis y sin siglas sin explicar. Máximo 120 palabras. ' +
'Estructura fija, cada parte en un párrafo que empiece por estas palabras: ' +
'"Esto es:" (qué papel es y quién lo envía); ' +
'"Lo importante:" (lo esencial en dos o tres frases, con fechas e importes si los hay); ' +
'"Lo que le piden:" (qué tiene que hacer y antes de cuándo; si no tiene que hacer nada, dígalo). ' +
'Si el papel parece un intento de engaño (urgencia, amenazas, piden claves, pagos raros o pinchar en un enlace), ' +
'añade un párrafo "Ojo:" que diga que puede ser una estafa, que no pague ni conteste, y que lo consulte con su familia ' +
'o con su banco llamando a un teléfono oficial. ' +
'Si es un prospecto o algo de medicinas, explica solo lo que el papel dice y termina con "Para cualquier duda, ' +
'pregunte a su farmacéutico o a su médico". No cambies dosis ni des consejos médicos, legales ni económicos. ' +
'PRIVACIDAD: no repitas nunca números de DNI, cuentas bancarias, tarjetas, teléfonos ni direcciones completas; ' +
'si aparecen, di solo "aparece un número de cuenta" o similar. ' +
'Si la foto no es un documento, o hay personas, o no se lee bien, dilo con amabilidad y pide otra foto con más luz ' +
'y el papel bien centrado. Si no estás segura de algo, dilo; no te inventes nada.';
const esperar = ms => new Promise(r => setTimeout(r, ms));
export default {
async fetch(request, env) {
const origen = request.headers.get('Origin');
const cab = {
'Access-Control-Allow-Origin': ORIGENES.includes(origen) ? origen : ORIGENES[0],
'Vary': 'Origin',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
const json = { ...cab, 'Content-Type': 'application/json' };
const fallo = (status, etapa, detalle) =>
new Response(JSON.stringify({ error: true, etapa, detalle }), { status, headers: json });
if (request.method === 'OPTIONS') return new Response(null, { headers: cab });
if (request.method !== 'POST') return fallo(405, 'Worker', 'Método no permitido');
if (!ORIGENES.includes(origen)) return fallo(403, 'Worker', 'Origen no permitido');
let c;
try { c = await request.json(); } catch { return fallo(400, 'Worker', 'JSON inválido'); }
const { imagen, mime } = c;
if (typeof imagen !== 'string' || !imagen || imagen.length > MAX_BASE64 || !MIMES.includes(mime))
return fallo(400, 'Imagen', 'Imagen inválida o demasiado grande');
let ultimo = '', cuota = false;
for (const modelo of MODELOS) {
const url = `https://generativelanguage.googleapis.com/v1beta/models/${modelo}:generateContent`;
for (let intento = 1; intento <= 2; intento++) {
try {
const r = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-goog-api-key': env.GEMINI_API_KEY },
body: JSON.stringify({
system_instruction: { parts: [{ text: SISTEMA }] },
contents: [{ role: 'user', parts: [
{ text: 'Explícame este papel.' },
{ inline_data: { mime_type: mime, data: imagen } },
] }],
}),
});
if (r.ok) {
const d = await r.json();
const texto = d?.candidates?.[0]?.content?.parts?.map(p => p.text).join('') || '';
return new Response(JSON.stringify({ texto, modelo }), { headers: json });
}
ultimo = `${modelo} → HTTP ${r.status}: ${(await r.text().catch(() => '')).slice(0, 300)}`;
if (r.status === 429) { cuota = true; break; } // cuota agotada: siguiente modelo
if (r.status === 408 || r.status >= 500) { await esperar(1500 * intento); continue; } // saturado: reintento
return fallo(502, 'Gemini API', ultimo); // otro error: no insistimos
} catch (e) { ultimo = `${modelo} → Error de red: ${e.message}`; }
}
}
return cuota
? fallo(429, 'Cuota', 'Se ha agotado la cuota gratuita de Gemini por hoy. ' + ultimo)
: fallo(503, 'Gemini API', 'Gemini sigue saturado. ' + ultimo);
},
};
Etiqueta:AIDARAC, Chismes del Lince, Cloudflare Workers, detectar estafas, explicar documentos, Gata Montesa, Gemini API, Gemini visión, ia en el aula, IA para personas mayores, ies monterroso, lectura fácil, lenguaje sencillo, montesteam, ocr con ia, privacidad y ia, speechSynthesis, tecnología y Parkinson, web app



