For my Catalan friends and Alexa · Pels meus companys catalans i Alexa
Alexa no té cap resum de notícies en català. Amb mitja hora i zero euros, te’n pots fer un amb el butlletí horari de 3CatInfo.
A note in Catalan for Catalan speakers: how to make an Amazon Echo play the hourly news bulletin from 3CatInfo (Catalunya Ràdio) when you say “Alexa, pon las noticias”, using a private Flash Briefing skill and a free AWS Lambda. The code below works for anyone.
A casa tenim Alexa configurada en castellà, perquè en català no n’hi ha. Cada matí li demanava «Alexa, pon las noticias» i em posava notícies en castellà d’arreu. Volia sentir 3CatInfo, i al catàleg de notícies d’Alexa no hi ha ni Catalunya Ràdio ni 3CatInfo.
La bona notícia és que no cal esperar que ningú ho publiqui. Alexa deixa que qualsevol persona es faci el seu propi resum de notícies (un Flash Briefing) en mode desenvolupament, sense publicar-lo, i 3Cat penja cada hora l’emissió de 3CatInfo en MP3. Només cal una peça petita que les uneixi. Tot plegat porta uns 30 minuts i costa 0 €.
Per què no surt Catalunya Ràdio a les notícies d’Alexa
El resum de notícies d’Alexa només ofereix les skills Flash Briefing publicades per al mercat i l’idioma del teu Echo. A amazon.es, en castellà, la CCMA no en té cap. I Alexa no parla català, així que tampoc hi ha un mercat català on buscar-la. Des de l’app no ho arreglaràs.
Com funciona la solució
- Dius «Alexa, pon las noticias».
- Alexa consulta la teva skill Flash Briefing privada.
- La skill llegeix un petit feed JSON que serveix una funció AWS Lambda.
- La Lambda pregunta a l’API pública de 3Cat quina és l’última hora de 3CatInfo publicada (el programa 710, «Notícies 3CatInfo – hora a hora»).
- Alexa reprodueix els primers ~4 minuts d’aquella hora: el butlletí d’inici d’hora.
Alguns detalls:
- Frescor. 3Cat publica l’hora sencera en MP3 uns 10–15 minuts després d’acabar. Les notícies que sentiràs tenen entre 15 minuts i unes 2 hores.
- El retall. L’MP3 dura una hora. La Lambda en demana només els primers 4 MB amb una petició de rang (128 kbps = 16 KB/s, uns 250 segons).
- Per què no els «Titulars». 3Cat té també un àudio curt de titulars de Catalunya Informació, però el seu MP3 no es renova de manera fiable. Les emissions horàries sí.
- Seguretat. La ruta d’àudio només accepta fitxers MP3 de 3Cat, perquè la Lambda no es converteixi en un proxy obert.
Què necessites
| Què | Per què | Nota |
|---|---|---|
| Compte d’Amazon dels Echo (amazon.es) | Entrar al developer console i a l’app Alexa | Ha de ser el mateix compte en tots dos llocs |
| Compte d’AWS | Allotjar la Lambda | Capa gratuïta: 1 milió de peticions al mes |
| Una imatge de 512 × 512 (PNG o JPG) | Icona del feed | Qualsevol imatge serveix |
| AWS CLI (opcional) | Fer-ho tot des del terminal | També es pot fer des de la consola web |
Regió d’AWS recomanada: eu-west-1 (Irlanda), la més propera als servidors europeus d’Alexa.
Pas 1 · Crear la Lambda
Una funció Python 3.13, 256 MB de memòria i 30 segons de timeout. No necessita cap llibreria externa.
Des de la consola web: AWS Console → Lambda → regió eu-west-1 → Create function → Author from scratch:
- Nom:
flash_briefing_3catinfo. - Runtime: Python 3.13.
- Permisos: Create a new role with basic Lambda permissions.
- Enganxa el codi a
lambda_function.pyi prem Deploy. - A Configuration → General configuration, posa 256 MB i 30 s.
"""Feed Flash Briefing d'Alexa amb el butlletí horari de 3CatInfo.
Lambda amb Function URL pública (auth NONE), dos camins:
GET / -> JSON Flash Briefing amb l'hora més recent de 3CatInfo
GET /audio -> primers ~4 min d'aquell MP3 (butlletí d'inici d'hora)
"""
import base64
import json
import os
import re
import urllib.request
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
API_URL = (
"https://api.3cat.cat/audios?_format=json&programaradio_id=710"
"&items_pagina=6&ordre=-data_publicacio&version=2.0"
)
MP3_BASE = "https://audios.3catvideos.cat/multimedia/"
# Només rutes d'MP3 de 3Cat: evita que /audio sigui un proxy obert.
MP3_PATH_RE = re.compile(r"^mp3/\d/\d/\d+\.mp3$")
# Emissions horàries ("3CatInfo, de 18 a 19 h - 22/09/2026").
HOURLY_TITLE_RE = re.compile(r"^3CatInfo, d.+ h - \d{2}/\d{2}/\d{4}$")
CLIP_BYTES = int(os.environ.get("CLIP_BYTES", "4000000")) # ~250 s
MADRID = ZoneInfo("Europe/Madrid")
USER_AGENT = "alexa-flash-briefing/1.0"
def _get(url, headers=None, timeout=10):
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT, **(headers or {})})
with urllib.request.urlopen(req, timeout=timeout) as resp:
return resp.read()
def _latest_hour():
items = json.loads(_get(API_URL))["resposta"]["items"]["item"]
for item in items:
audios = item.get("audios") or []
if HOURLY_TITLE_RE.match(item.get("titol", "")) and audios:
path = audios[0]["text"]
if MP3_PATH_RE.match(path):
return item, path
raise RuntimeError("No s'ha trobat cap emissió horària de 3CatInfo")
def _feed(event):
item, path = _latest_hour()
start = datetime.strptime(item["data_publicacio"], "%d/%m/%Y %H:%M:%S").replace(tzinfo=MADRID)
host = event["requestContext"]["domainName"]
body = {
"uid": f"3catinfo-{item['id']}",
"updateDate": start.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.0Z"),
"titleText": item["titol"],
"mainText": "",
"streamUrl": f"https://{host}/audio?src={path}",
"redirectionUrl": "https://www.3cat.cat/3cat/directes/3catinfo-radio/",
}
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json; charset=utf-8", "Cache-Control": "max-age=300"},
"body": json.dumps(body, ensure_ascii=False),
}
def _audio(event):
path = (event.get("queryStringParameters") or {}).get("src", "")
if not MP3_PATH_RE.match(path):
return {"statusCode": 400, "body": "src invàlid"}
data = _get(MP3_BASE + path, headers={"Range": f"bytes=0-{CLIP_BYTES - 1}"}, timeout=20)[:CLIP_BYTES]
return {
"statusCode": 200,
"headers": {"Content-Type": "audio/mpeg", "Cache-Control": "max-age=86400"},
"isBase64Encoded": True,
"body": base64.b64encode(data).decode("ascii"),
}
def lambda_handler(event, context):
route = event.get("rawPath", "/")
try:
if route == "/audio":
return _audio(event)
return _feed(event)
except Exception as exc:
print(f"ERROR {route}: {exc!r}")
return {"statusCode": 502, "body": "3Cat no disponible"}Des del terminal (alternativa), amb el codi desat a lambda_function.py i un rol que tingui la política AWSLambdaBasicExecutionRole:
zip -j fb.zip lambda_function.py
aws lambda create-function --region eu-west-1 \
--function-name flash_briefing_3catinfo \
--runtime python3.13 --handler lambda_function.lambda_handler \
--role arn:aws:iam::<EL_TEU_COMPTE>:role/<EL_TEU_ROL> \
--zip-file fileb://fb.zip --memory-size 256 --timeout 30Pas 2 · Donar-li una URL pública i provar-la
Alexa ha de poder llegir el feed sense autenticació, així que la Lambda necessita una Function URL amb autenticació NONE.
Consola web: a la funció → Configuration → Function URL → Create function URL → Auth type NONE → Save. La consola ja hi afegeix els permisos públics.
Terminal:
F=flash_briefing_3catinfo; R=eu-west-1
aws lambda create-function-url-config --region $R --function-name $F --auth-type NONE
aws lambda add-permission --region $R --function-name $F --statement-id public-url \
--action lambda:InvokeFunctionUrl --principal '*' --function-url-auth-type NONE
aws lambda add-permission --region $R --function-name $F --statement-id public-url-invoke \
--action lambda:InvokeFunction --principal '*' --invoked-via-function-urlObtindràs una URL del tipus https://<id>.lambda-url.eu-west-1.on.aws/. Prova-la:
curl https://<id>.lambda-url.eu-west-1.on.aws/Ha de tornar un JSON amb un titleText com «3CatInfo, de 18 a 19 h – …» i un streamUrl. Obre el streamUrl al navegador: hauries de sentir uns 4 minuts de 3CatInfo.
Pas 3 · Crear la skill Flash Briefing
La skill es queda en mode desenvolupament: no l’has d’enviar a certificació ni publicar-la, i funciona als Echo del teu compte.
- Entra a developer.amazon.com/alexa/console/ask amb el compte d’amazon.es dels teus Echo.
- Create Skill → nom
Noticies3CatInfo→ idioma Spanish (ES) → tipus Flash Briefing. - Custom error message:
No he podido cargar las noticias de 3CatInfo. - Add new feed, amb aquests valors:
| Camp | Valor |
|---|---|
| Preamble | De 3CatInfo (en castellà ha de començar per «En» o «De») |
| Name | 3CatInfo |
| Content update frequency | Hourly |
| Content type | Audio |
| Content genre | News |
| Feed | La URL de la Lambda del pas 2 |
| Feed icon | La teva imatge de 512 × 512 |
- Save. En aquest moment Amazon crida el feed per validar-lo.
- Pestanya Test → Skill testing is enabled in: Development. Al simulador, amb Spanish (ES), escriu
pon las noticias: hauries de sentir el butlletí.
A la pestanya Test la skill no surt llistada enlloc, i el missatge «Skill I/O is available only…» és normal en un Flash Briefing.
Pas 4 · Activar-la a l’app Alexa
- App Alexa → Settings → Preferences → News (en castellà: Configuración → Preferencias → Noticias).
- Add content → busca
Noticies3CatInfoi activa-la. - Posa-la la primera, o treu les altres fonts si només vols 3CatInfo.
- Digues «Alexa, pon las noticias».
Si no trobes l’apartat News, escriu «news settings» al quadre Ask Alexa about settings de la mateixa pantalla, o busca la skill a More → Skills & Games → Your Skills → Dev.
Problemes habituals
| Problema | Solució |
|---|---|
| El Preamble no s’accepta | En castellà ha de començar per «En» o «De»: De 3CatInfo |
| La skill no surt a l’app | L’app i el developer console han d’estar amb el mateix compte d’Amazon; espera uns minuts que es propagui |
| Alexa+ respon amb el seu propi resum | Prova «Alexa, pon mi resumen de noticias» o «Alexa, ¿qué hay de nuevo?» |
| Les notícies semblen d’una hora enrere | És normal: 3Cat publica l’hora sencera 10–15 minuts després d’acabar, i Alexa guarda el feed uns minuts |
| El butlletí es talla massa aviat o tard | Canvia la variable d’entorn CLIP_BYTES (16.000 bytes per segon; màxim uns 4.400.000, pel límit de 6 MB de Lambda) |
| Error «3Cat no disponible» | 3Cat ha canviat l’API o els títols: mira els logs de la Lambda a CloudWatch |
L’àudio és de 3Cat (CCMA) i només l’escoltes tu, als teus dispositius. Aquesta guia no redistribueix cap contingut: la Lambda només retalla, per a ús privat, l’MP3 públic de l’última hora.
Si ho muntes i t’hi trobes algun problema, escriu-me. I si a la CCMA hi ha algú llegint això: una skill oficial de Flash Briefing de 3CatInfo per a Alexa faria feliç molta gent.