# Codex — Implementación v0.1 Skill Radio Babasónica

## Objetivo de esta iteración

Implementar en el servidor Windows 10 + XAMPP un backend mínimo, separado y seguro para una **Alexa Custom Skill** llamada **Radio Babasónica**.

La v0.1 debe tener un único objetivo funcional:

```text
"Alexa, abre Radio Babasónica"
        ↓
Alexa responde brevemente
        ↓
reproduce la señal en vivo `radiobbs`
```

Señal oficial para esta versión:

```text
https://aproposito.ddns.net/live/radiobbs/index.m3u8
```

NO usar `?cookieCheck=1` en la URL que se envía a Alexa.

La señal ya fue diagnosticada externamente y se confirmó:

```text
HTTPS: OK
Puerto 443: OK
TLS / Let's Encrypt: OK
HLS/M3U8: OK
Audio-only: OK
Codec: AAC-LC
Sample rate: 48 kHz
Canales: stereo
Bitrate observado: ~127–129 kbps
Segmentos HTTPS: OK
Lectura continua 20 s: OK
Lectura continua 60 s: OK
```

---

# Principios obligatorios

1. **No modificar SignalLive.**
2. **No modificar MediaMTX.**
3. **No modificar `mediamtx.yml`.**
4. **No modificar los proxies existentes `/live/`.**
5. **No reiniciar MediaMTX ni los streamers.**
6. **No cambiar la señal `radiobbs`.**
7. **No cambiar FFmpeg ni la codificación de la radio.**
8. **No modificar la app C# ni WordPress.**
9. **No tocar la Skill del Mundial salvo para inspeccionar/reutilizar patrones técnicos de Alexa.**
10. Esta nueva Skill debe quedar desacoplada de la lógica de la Skill del Mundial.
11. No publicar secretos, tokens, contraseñas, claves privadas o certificados privados en reportes.
12. Si para aplicar una configuración de Apache fuese imprescindible reiniciarlo, **NO hacerlo automáticamente**. Documentar el cambio requerido y detenerse antes del reinicio.

---

# Paso 1 — Inspeccionar antes de crear

Antes de escribir código:

## 1.1 Buscar implementaciones Alexa existentes

Buscar dentro de `C:\xampp\htdocs\` archivos/carpetas relacionados con la Skill del Mundial o Alexa, incluyendo nombres y referencias como:

```text
AlexaController.php
alexa
worldcup
worldcup-api
skill
LaunchRequest
AudioPlayer
AMAZON.PauseIntent
requestEnvelope
applicationId
SignatureCertChainUrl
Signature
```

Objetivo:

- identificar cómo recibe actualmente peticiones Alexa el servidor;
- localizar cualquier validación de requests Alexa ya implementada;
- localizar convenciones de controllers, responses, logging y bootstrap;
- reutilizar **infraestructura genérica** cuando sea segura y apropiada;
- NO reutilizar lógica funcional del Mundial.

Registrar en el informe qué archivos fueron encontrados y cuáles se tomaron como referencia.

## 1.2 Confirmar PHP

Ejecutar:

```cmd
C:\xampp\php\php.exe -v
```

Registrar versión.

## 1.3 Confirmar DocumentRoot / VirtualHost

Inspeccionar configuración Apache necesaria para determinar cómo exponer un endpoint PHP por:

```text
https://aproposito.ddns.net/...
```

No modificar todavía.

---

# Paso 2 — Carpeta del proyecto

Si ya existe una carpeta dedicada para **Radio Babasónica Alexa**, utilizarla.

Si NO existe, crear:

```text
C:\xampp\htdocs\radio-babasonica-alexa\
```

Estructura recomendada:

```text
radio-babasonica-alexa\
│
├── public\
│   └── index.php
│
├── src\
│   ├── AlexaRequestHandler.php
│   ├── RadioBabasónicaController.php
│   └── AlexaResponse.php
│
├── config\
│   └── config.php
│
├── storage\
│   └── logs\
│
├── interaction-model\
│   └── es-MX.json
│
├── tests\
│   └── fixtures\
│
├── README.md
└── .gitignore
```

Los nombres pueden adaptarse a las convenciones que ya existan en los proyectos del usuario.

Preferir diseño OOP sencillo y legible.

No añadir frameworks pesados si no son necesarios.

---

# Paso 3 — Configuración central

Definir la señal en UN solo lugar de configuración:

```php
RADIO_BABASONICA_STREAM_URL =
https://aproposito.ddns.net/live/radiobbs/index.m3u8
```

Definir también un token estable para Alexa:

```text
radiobbs-live-v1
```

No duplicar la URL en múltiples controllers.

Preparar configuración opcional para:

```text
ALEXA_SKILL_ID
```

El Skill ID puede todavía no conocerse.

Si aún no existe, permitir dejarlo pendiente de configuración y documentar claramente dónde colocarlo.

No inventar un Skill ID.

---

# Paso 4 — LaunchRequest

Cuando llegue:

```text
LaunchRequest
```

la Skill debe responder con:

```text
Estás escuchando Radio Babasónica.
```

y enviar una directiva:

```json
{
  "type": "AudioPlayer.Play",
  "playBehavior": "REPLACE_ALL",
  "audioItem": {
    "stream": {
      "token": "radiobbs-live-v1",
      "url": "https://aproposito.ddns.net/live/radiobbs/index.m3u8",
      "offsetInMilliseconds": 0
    }
  }
}
```

La respuesta debe tener:

```json
"shouldEndSession": true
```

La locución debe ocurrir antes de empezar el stream.

---

# Paso 5 — Intent para reproducir la radio

Crear un intent propio, por ejemplo:

```text
PlayRadioIntent
```

Debe realizar exactamente la misma acción que `LaunchRequest`.

Samples sugeridos para `es-MX`:

```text
pon la radio
pon radio babasónica
reproduce la radio
reproduce radio babasónica
quiero escuchar la radio
quiero escuchar radio babasónica
escuchar la radio
escuchar radio babasónica
enciende la radio
```

Evitar sobrecargar el modelo.

---

# Paso 6 — Pause

Implementar obligatoriamente:

```text
AMAZON.PauseIntent
```

Respuesta:

```json
{
  "type": "AudioPlayer.Stop"
}
```

No intentar mantener un offset de una emisión en vivo.

No guardar posición para la radio.

---

# Paso 7 — Resume

Implementar obligatoriamente:

```text
AMAZON.ResumeIntent
```

Como `radiobbs` es una emisión en vivo, "reanudar" significa volver al **punto actual en vivo**, no continuar desde un timestamp antiguo.

Enviar nuevamente:

```text
AudioPlayer.Play
playBehavior = REPLACE_ALL
URL = radiobbs
offsetInMilliseconds = 0
```

No intentar reconstruir un offset histórico.

---

# Paso 8 — Stop / Cancel

Manejar como mínimo:

```text
AMAZON.StopIntent
AMAZON.CancelIntent
```

Detener la reproducción mediante:

```text
AudioPlayer.Stop
```

Opcionalmente limpiar la cola si el diseño de la implementación lo requiere, pero no crear comportamiento complejo innecesario.

---

# Paso 9 — Intents recomendados que no aplican

La radio v0.1 NO tendrá playlist navegable.

Manejar sin errores:

```text
AMAZON.NextIntent
AMAZON.PreviousIntent
AMAZON.StartOverIntent
AMAZON.RepeatIntent
AMAZON.ShuffleOnIntent
AMAZON.ShuffleOffIntent
AMAZON.LoopOnIntent
AMAZON.LoopOffIntent
```

No inventar canciones ni saltos.

Responder de forma breve y apropiada cuando sean IntentRequests normales, por ejemplo:

```text
Esta versión de Radio Babasónica reproduce la señal en vivo.
```

Si Amazon requiere una forma particular para alguno de estos contextos, respetarla.

---

# Paso 10 — AudioPlayer events

El endpoint debe reconocer y manejar sin errores:

```text
AudioPlayer.PlaybackStarted
AudioPlayer.PlaybackStopped
AudioPlayer.PlaybackFinished
AudioPlayer.PlaybackNearlyFinished
AudioPlayer.PlaybackFailed
```

## PlaybackStarted

Registrar:

```text
requestId
token
offset
timestamp
```

No hablar.

## PlaybackStopped

Registrar:

```text
requestId
token
offset
timestamp
```

No hablar.

## PlaybackFinished

Registrar evento.

Para un stream live normalmente no debería finalizar por sí solo.

No iniciar loops ciegos.

## PlaybackNearlyFinished

Para esta señal live no agregar elementos a una cola de canciones.

Responder correctamente sin producir errores.

## PlaybackFailed

Registrar especialmente:

```text
token
playerActivity
error.type
error.message
requestId
timestamp
```

No crear un retry infinito.

El log de `PlaybackFailed` será crucial para la primera prueba con Echo físico.

---

# Paso 11 — PlaybackController

Manejar:

```text
PlaybackController.PlayCommandIssued
PlaybackController.PauseCommandIssued
PlaybackController.NextCommandIssued
PlaybackController.PreviousCommandIssued
```

Reglas:

### PlayCommandIssued

Volver a enviar:

```text
AudioPlayer.Play
REPLACE_ALL
radiobbs
offset 0
```

### PauseCommandIssued

Enviar:

```text
AudioPlayer.Stop
```

### Next / Previous

La v0.1 no tiene siguiente/anterior.

Responder únicamente de una forma permitida por `PlaybackController`.

**IMPORTANTE:** las respuestas a `PlaybackController` NO deben incluir:

```text
outputSpeech
card
reprompt
```

Sólo directivas `AudioPlayer` cuando corresponda.

---

# Paso 12 — Respuestas AudioPlayer sin `session`

No asumir que todos los requests contienen:

```json
"session"
```

`AudioPlayer.*` y `PlaybackController.*` pueden llegar sin objeto `session`.

Leer application/user/device desde:

```text
context.System
```

cuando corresponda.

La implementación no debe romperse por ausencia de `session`.

---

# Paso 13 — Verificación de requests Alexa

Esto es importante.

Antes de implementar un verificador nuevo desde cero:

1. revisar la Skill del Mundial;
2. determinar si ya existe una implementación confiable de validación de peticiones Alexa;
3. reutilizarla o extraerla a una utilidad compartida si es razonable.

Validar, según corresponda:

```text
SignatureCertChainUrl
Signature
timestamp
applicationId / Skill ID
```

No desactivar permanentemente la validación sólo para facilitar pruebas.

Si la infraestructura existente no valida requests Alexa, documentarlo claramente como:

```text
BLOQUEO DE SEGURIDAD / CERTIFICACIÓN
```

y construir la implementación correcta antes de considerar la v0.1 terminada.

No inventar certificados.

No almacenar certificados privados de Amazon.

---

# Paso 14 — Logging

Crear log dedicado, por ejemplo:

```text
storage\logs\alexa-radio.log
```

Registrar por request:

```text
fecha/hora
requestId
request.type
intent.name si existe
locale
applicationId (puede abreviarse)
AudioPlayer token
AudioPlayer playerActivity
resultado
error si existe
```

NO registrar:

```text
accessToken
apiAccessToken
cookies privadas
headers de autorización completos
secretos
```

Para la primera versión se permite logging detallado de estructura funcional, pero sanitizado.

---

# Paso 15 — Endpoint HTTP

El endpoint debe:

1. aceptar únicamente el método necesario para Alexa (POST);
2. responder JSON con:
   ```text
   Content-Type: application/json
   ```
3. manejar JSON inválido con respuesta controlada;
4. no imprimir warnings/notices antes del JSON;
5. no revelar stack traces al cliente;
6. escribir errores técnicos en log;
7. usar status HTTP razonables;
8. no depender de sesión PHP del navegador.

---

# Paso 16 — Interaction Model `es-MX`

Crear un modelo importable para Alexa Developer Console.

Invocation name candidato:

```text
radio babasónica
```

Mantener el acento en `babasónica`.

Locale:

```text
es-MX
```

Incluir al menos:

```text
PlayRadioIntent
AMAZON.PauseIntent
AMAZON.ResumeIntent
AMAZON.StopIntent
AMAZON.CancelIntent
AMAZON.HelpIntent
AMAZON.FallbackIntent
AMAZON.NextIntent
AMAZON.PreviousIntent
AMAZON.StartOverIntent
AMAZON.RepeatIntent
AMAZON.ShuffleOnIntent
AMAZON.ShuffleOffIntent
AMAZON.LoopOnIntent
AMAZON.LoopOffIntent
```

No agregar slots en v0.1.

No incluir todavía:

```text
podcasts
audiolibros
tour
noticias
qué está sonando
usuarios
cuentas
persistencia
```

---

# Paso 17 — Help / Fallback

## AMAZON.HelpIntent

Respuesta sugerida:

```text
Puedes decir: pon Radio Babasónica.
```

## AMAZON.FallbackIntent

Respuesta breve orientada al alcance actual:

```text
Por ahora puedo reproducir Radio Babasónica. Puedes decir: pon la radio.
```

---

# Paso 18 — Test fixtures locales

Crear JSON fixtures para probar al menos:

```text
LaunchRequest
PlayRadioIntent
AMAZON.PauseIntent
AMAZON.ResumeIntent
AMAZON.StopIntent
AudioPlayer.PlaybackStarted
AudioPlayer.PlaybackStopped
AudioPlayer.PlaybackFailed
PlaybackController.PlayCommandIssued
PlaybackController.PauseCommandIssued
```

No usar datos sensibles reales.

Crear un script o procedimiento sencillo para enviar los fixtures al handler local.

Si la validación de firma impide fixtures locales, separar claramente:

```text
handler lógico
```

de:

```text
verificador de transporte Alexa
```

de modo que la lógica pueda probarse sin debilitar la seguridad del endpoint público.

---

# Paso 19 — Validaciones automáticas

Comprobar que `LaunchRequest` genere:

```text
AudioPlayer.Play
REPLACE_ALL
radiobbs-live-v1
https://aproposito.ddns.net/live/radiobbs/index.m3u8
offsetInMilliseconds = 0
shouldEndSession = true
```

Comprobar que Pause genere:

```text
AudioPlayer.Stop
```

Comprobar que Resume vuelva a generar Play hacia el stream live.

Comprobar que PlaybackController responses no lleven `outputSpeech`.

Comprobar que eventos sin `session` no arrojen errores PHP.

---

# Paso 20 — Prueba de la señal desde el backend

Sin modificarla, confirmar antes de cerrar la implementación:

```cmd
C:\ffmpeg\bin\ffprobe.exe -hide_banner -v error -show_streams "https://aproposito.ddns.net/live/radiobbs/index.m3u8"
```

Confirmar que sigue siendo:

```text
AAC
48 kHz
stereo
audio-only
```

---

# Paso 21 — URL pública del webhook

Determinar una URL HTTPS limpia para el endpoint.

Preferencia conceptual:

```text
https://aproposito.ddns.net/radio-babasonica-alexa/
```

o:

```text
https://aproposito.ddns.net/radio-babasonica-alexa/public/
```

Elegir la opción que encaje correctamente con la configuración Apache existente.

NO crear una nueva regla bajo:

```text
/live/
```

porque `/live/` pertenece al streaming MediaMTX.

La ruta Alexa debe estar fuera de `/live/`.

Probar:

```text
HTTPS
POST
Content-Type
certificado
status
JSON
```

Si se requiere una modificación Apache para exponer la carpeta de manera limpia, prepararla y documentarla, pero no reiniciar Apache sin intervención del usuario.

---

# Paso 22 — No configurar todavía Alexa Developer Console

Codex NO debe intentar acceder a la cuenta de Amazon ni configurar la Skill en Developer Console.

Debe dejar listos:

```text
backend
URL pública
interaction model
instrucciones
```

Después ChatGPT guiará al usuario por Alexa Developer Console.

---

# Paso 23 — README

Crear `README.md` con:

```text
Nombre del proyecto
Objetivo v0.1
Arquitectura
URL del stream
URL del webhook
Requisitos
Cómo probar localmente
Cómo consultar logs
Cómo actualizar Skill ID
Archivos principales
Qué NO cubre esta versión
```

---

# Paso 24 — Reporte final obligatorio

Generar:

```text
C:\xampp\htdocs\radio-babasonica-alexa\RADIO_BABASONICA_ALEXA_V01_IMPLEMENTATION_RESULTS.md
```

Si finalmente se usó otra carpeta porque ya existía un proyecto dedicado, guardar el reporte en la raíz real de ese proyecto y especificar su ruta.

El reporte debe incluir:

## Resumen

```text
Implementación completada: SI / PARCIAL / NO
Endpoint público:
Stream:
PHP:
Skill ID configurado: SI / NO
Verificación Alexa: SI / NO / PENDIENTE
```

## Archivos creados

Lista completa.

## Archivos modificados

Lista completa.

## Archivos inspeccionados de otros proyectos

Lista completa y motivo.

## Arquitectura resultante

Diagrama ASCII.

## Interaction Model

Ruta del JSON.

## Pruebas ejecutadas

Tabla:

| Prueba | Resultado |
|---|---|
| LaunchRequest | |
| PlayRadioIntent | |
| PauseIntent | |
| ResumeIntent | |
| StopIntent | |
| PlaybackStarted | |
| PlaybackStopped | |
| PlaybackFailed | |
| PlaybackController Play | |
| PlaybackController Pause | |
| JSON inválido | |
| Stream ffprobe | |
| Endpoint HTTPS | |

## Respuesta exacta de LaunchRequest

Pegar el JSON producido.

## Respuesta exacta de PauseIntent

Pegar el JSON producido.

## Respuesta exacta de ResumeIntent

Pegar el JSON producido.

## Seguridad

Describir:

```text
validación de firma
validación timestamp
validación Skill ID
sanitización logs
```

## Cambios manuales pendientes

Especificar exactamente qué debe hacer el usuario.

Por ejemplo:

```text
1. Crear Custom Skill en Alexa Developer Console.
2. Seleccionar es-MX.
3. Invocation name: radio babasónica.
4. Importar interaction-model/es-MX.json.
5. Activar AudioPlayer.
6. Configurar endpoint HTTPS: ...
7. Colocar Skill ID en ...
```

No asumir que estos pasos ya fueron realizados.

## Riesgos / pendientes

Incluir cualquier aspecto que requiera prueba real con Echo.

---

# Criterios de aceptación v0.1

La implementación puede considerarse lista para pasar a Alexa Developer Console únicamente si:

```text
[ ] LaunchRequest produce AudioPlayer.Play válido
[ ] Stream URL es la limpia sin cookieCheck
[ ] shouldEndSession = true en Play
[ ] Pause funciona
[ ] Resume vuelve al live edge
[ ] Stop funciona
[ ] AudioPlayer events no rompen el endpoint
[ ] PlaybackController no devuelve propiedades prohibidas
[ ] Endpoint acepta requests sin objeto session
[ ] Logs están sanitizados
[ ] No se modificó MediaMTX
[ ] No se modificó radiobbs
[ ] Interaction Model es-MX quedó generado
[ ] URL HTTPS del webhook quedó identificada
[ ] Verificación Alexa está implementada o claramente marcada como bloqueo
[ ] Reporte final fue generado
```

---

# Documentación oficial de referencia

Usar como referencia principal la documentación oficial actual de Amazon:

```text
https://developer.amazon.com/en-US/docs/alexa/custom-skills/use-long-form-audio.html
https://developer.amazon.com/en-US/docs/alexa/custom-skills/audioplayer-interface-reference.html
https://developer.amazon.com/en-US/docs/alexa/custom-skills/playback-controller-interface-reference.html
https://developer.amazon.com/en-US/docs/alexa/custom-skills/choose-the-invocation-name-for-a-custom-skill.html
```

Puntos especialmente importantes:

```text
AudioPlayer.Play → shouldEndSession = true
AMAZON.PauseIntent → obligatorio
AMAZON.ResumeIntent → obligatorio
AudioPlayer requests pueden llegar sin session
PlaybackController responses no pueden llevar outputSpeech/card/reprompt
```

Si la documentación oficial contradice cualquier detalle menor de este documento, seguir la documentación oficial y registrar la diferencia en el reporte.

---

# Alcance excluido de v0.1

NO implementar todavía:

```text
Podcasts
Audiolibros
Noticias
Tour
"Qué está sonando"
Carátulas dinámicas
Persistencia por usuario
Favoritos
Cuentas
Playlists
Siguiente canción
Canción anterior
Analytics complejos
Monetización
```

Primero debemos conseguir una prueba real exitosa:

```text
"Alexa, abre Radio Babasónica"
```

→ Alexa habla.

→ Inicia `radiobbs`.

→ Puede pausarse.

→ Puede reanudarse al vivo.

→ Puede detenerse.

Ése es el único objetivo de la v0.1.
