Marcos Ramírez BETA
Notas y un icono de vídeo de YouTube convergiendo por flechas hacia una tarjeta de GitHub Issue con etiqueta draft

Automatizo la captura de ideas del blog en GitHub Issues

· ⏱ 12+ min lectura

Tenía un problema tonto y muy real: las ideas para el blog se me escapaban.

Una se me ocurría en el coche y la apuntaba en el móvil. Otra venía de un vídeo que guardaba en YouTube “para verlo luego”. Otra la tecleaba en una nota que no volvía a abrir jamás. ¿El resultado? La mitad de las ideas buenas se perdían por el camino, y cuando me sentaba a escribir, me quedaba mirando la pantalla en blanco como si nunca hubiera tenido una idea en mi vida.

Así que dejé de pelearme con mi memoria y monté un sistema. Uno que recoge las ideas solo, las junte todas en el mismo sitio y no dependa de que yo me acuerde de nada.

La idea: una sola bandeja de entrada para todo

El principio es simple. Yo no quiero cambiar cómo apunto las cosas. Quiero seguir tirando ideas donde ya las tiro: una tarea rápida cuando se me ocurre algo, un vídeo guardado cuando veo algo que me inspira. Lo que quiero es que todo eso acabe en el mismo cajón, sin que yo mueva un dedo.

Ese cajón es GitHub Issues. Cada idea se convierte en una issue con la etiqueta draft, y ya está. Cuando me siento a escribir, abro el repo, miro las issues con esa etiqueta y elijo. Ahí está todo. Sin notas perdidas, sin pestañas abiertas desde hace tres semanas.

¿Por qué GitHub Issues y no Notion, o Trello, o una nota gigante? Por una razón muy egoísta: el blog ya vive en GitHub. Los posts son archivos en el repo. Si las ideas viven al lado del contenido, todo está en el mismo flujo. Una idea es una issue, escribir el post la cierra. Cero herramientas nuevas que mantener, cero suscripciones, cero sincronizaciones raras entre apps que un día deciden dejar de hablarse.

Las dos fuentes: cómo apunto ideas de verdad

El sistema lee de dos sitios, que son los dos sitios donde realmente apunto cosas.

Fuente 1: una lista de Google Tasks llamada “Posts”. Cuando se me ocurre algo, lo dicto o lo escribo ahí en dos segundos. “Negocio de barrio, pagar en efectivo”. “Importancia de establecer procedimientos”. Frases sueltas, semillas. No me paro a desarrollarlas: las suelto y sigo con mi vida.

Fuente 2: una lista de reproducción de YouTube llamada “Posts”. Cuando veo un vídeo que me da una idea para un post, le doy a guardar en esa lista. Una herramienta que descubro, una noticia, un tutorial que quiero comentar. El vídeo se queda ahí esperando.

Lo bonito es que ninguna de las dos cosas me saca de lo que estoy haciendo. Apuntar una tarea es instantáneo. Guardar un vídeo es un clic. El esfuerzo de capturar la idea es casi cero, que es exactamente como tiene que ser, porque si capturar cuesta, no capturas.

El script que lo pega todo: sync_ideas.py

Aquí entra el pegamento. Un script en Python que se llama sync_ideas.py y hace tres cosas en orden:

  1. Lee la lista “Posts” de Google Tasks y la lista “Posts” de YouTube.
  2. Por cada idea que encuentra, crea una issue en GitHub con la etiqueta draft.
  3. Limpia el origen para no reprocesar: marca la tarea como completada y borra el vídeo de la lista.

Tienes los cuatro archivos completos (el script, el generador de token, las dependencias y el workflow) en este gist, listos para copiar. Más abajo te dejo el paso a paso para montarlo tú.

Para hablar con GitHub uso PyGithub, que envuelve la API en algo cómodo de usar. Para Google, las librerías oficiales de cliente. Crear una issue acaba siendo una línea tan tonta como esta:

repo.create_issue(title=title, body=url, labels=["draft"])

En el caso de los vídeos, el título de la issue lleva un prefijo [YT] para que de un vistazo sepa de dónde viene la idea, y en el cuerpo meto el enlace al vídeo. Así, cuando abra la issue para escribir el post, tengo el vídeo a un clic en lugar de acordarme vagamente de “uno que vi sobre no sé qué”.

Marcar como hecho, cada fuente a su manera

El detalle importante, el que hace que el sistema no se vuelva loco, es cómo le digo a cada fuente que una idea ya está procesada.

En Google Tasks es fácil: marco la tarea como completada. Desaparece de la lista de pendientes y no la vuelvo a ver. En la siguiente pasada, el script ya ni la considera.

En YouTube no existe ese concepto de “completar”. Así que hago lo equivalente: borro el vídeo de la lista de reproducción. Para eso el script necesita permiso de escritura sobre tu cuenta de YouTube (el scope de lectura no basta), pero el resultado es el mismo que con las tareas: una vez convertido en issue, el vídeo sale de la lista y no se reprocesa. Las dos fuentes funcionan igual de cara a mí, aunque por dentro la mecánica sea distinta.

La red de seguridad: deduplicación

Aun marcando las fuentes, montar un cinturón además de los tirantes era lo prudente. Porque las automatizaciones fallan a mitad de camino, los procesos se cortan, y no quiero acabar con la misma idea convertida en issue cinco veces.

La solución es deduplicar por título. Antes de crear nada, el script se trae todas las issues que ya existen con la etiqueta draft, abiertas y cerradas, y se guarda sus títulos. Si una idea coincide con una issue que ya existe, la salta. Que cuente también las cerradas es clave: significa que aunque yo ya haya escrito el post (y cerrado la issue), esa idea no volverá a aparecer como nueva. Una idea procesada lo está para siempre.

Este tipo de pegamento entre APIs, que parece simple pero tiene su miga en los detalles de idempotencia y permisos, es justo lo que monto para empresas que tienen datos repartidos en cinco herramientas que no se hablan entre ellas.

El reloj: GitHub Actions cada hora

¿Quién ejecuta el script? Un workflow de GitHub Actions programado para correr cada hora. Instala las dependencias, lee los secrets y lanza sync_ideas.py. Nada más.

Y aquí viene algo que igual te chirría si me lees de antes: hace poco escribí un post entero sobre por qué el cron de GitHub Actions no es de fiar y cómo me llevé el reloj a Cloudflare. ¿No me estoy contradiciendo usando ese mismo cron aquí?

No. Y la diferencia es el motivo por el que conviene pensar antes de copiar soluciones de un sitio a otro.

Allí, la hora importaba al minuto: un post tenía que salir a las 8:30 en punto, y un cron que se retrasa una hora arruinaba la publicación. Aquí no. Esto es una bandeja de entrada de ideas. Me da exactamente igual que una idea tarde una hora o tres en convertirse en issue, porque no la voy a mirar hasta dentro de varios días. El cron “best effort” de GitHub, que para publicar era un desastre, para esto sobra de largo. La fiabilidad que necesitas depende de para qué lo necesitas.

Los secrets, fuera del código

Para que el script hable con Google y con GitHub hacen falta credenciales, y esas credenciales no se escriben en ningún archivo del repositorio. Van como secrets del repo, cifradas, y el workflow las inyecta como variables de entorno en tiempo de ejecución.

Son tres para Google: el CLIENT_ID, el CLIENT_SECRET y un REFRESH_TOKEN de OAuth que generé una sola vez en local con un script aparte. Ese refresh token es lo que permite al workflow autenticarse como yo sin que yo esté delante. El token de GitHub no hace falta ni crearlo: Actions provee uno automáticamente en cada ejecución, con permiso para crear issues en su propio repo.

La regla aquí es la de siempre: una credencial en un archivo del repo es una credencial regalada. Si el repo es público, hay bots rastreando secrets a todas horas. Cifradas y fuera del código, punto.

Cómo montarlo tú paso a paso

Si quieres replicarlo en tu blog (o para cualquier otra cosa que se te ocurra capturar), aquí va el camino completo. El código está en el gist: cuatro archivos que copias tal cual.

  1. Crea las dos listas “Posts”. Una lista en Google Tasks llamada exactamente “Posts” y una lista de reproducción en YouTube con el mismo nombre. El script las busca por ese nombre, así que respétalo (o cámbialo en el código si prefieres otro).

  2. Activa las APIs en Google Cloud. En un proyecto de Google Cloud, habilita la Google Tasks API y la YouTube Data API v3. Para este volumen son gratuitas.

  3. Crea credenciales OAuth de tipo “App de escritorio”. En la sección de credenciales del proyecto, genera un ID de cliente OAuth de tipo escritorio: te da un client ID y un client secret. En la pantalla de consentimiento, añádete como usuario de prueba, que si no el acceso te saldrá bloqueado cuando vayas a autorizar.

  4. Genera el refresh token una vez, en local. Con get_token.py, exporta el client ID y el secret como variables de entorno y ejecútalo. Se abre el navegador, das tu consentimiento (acceso a Tasks y a YouTube con escritura, que es lo que permite borrar vídeos de la lista) y el script te imprime un refresh token. Ese token es lo que deja al workflow autenticarse como tú sin estar tú delante.

pip install -r requirements.txt
GOOGLE_CLIENT_ID=tu_id GOOGLE_CLIENT_SECRET=tu_secret python get_token.py
  1. Mete tres secrets en tu repo de GitHub. En Settings → Secrets and variables → Actions, añade GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET y GOOGLE_REFRESH_TOKEN. El token de GitHub no lo añades: Actions ya provee uno con permiso para crear issues en su propio repo.

  2. Copia los archivos y deja que el cron trabaje. Pon sync_ideas.py, requirements.txt y el workflow en su sitio. A partir de ahí, GitHub Actions ejecuta el script cada hora. La primera pasada te vaciará de golpe todo lo que lleves acumulado.

Y ya está. Desde ese momento, lo único que tienes que hacer es apuntar una tarea o guardar un vídeo. El resto va solo.

El resultado: una bandeja que se llena sola

Lo monté, le di al botón, y la primera ejecución me creó de golpe más de cuarenta issues con todas las ideas que llevaba meses acumulando entre tareas y vídeos guardados. Cuarenta semillas de post que estaban ahí, dispersas, a punto de perderse.

Ahora el ciclo es invisible. Se me ocurre algo, lo apunto en Tasks. Veo un vídeo que me inspira, lo guardo en la lista. Y sin que yo haga nada más, en la siguiente hora eso aparece en mi repo, etiquetado y listo para cuando tenga ganas de escribir. La fuente se limpia sola, los duplicados no entran, y yo no toco nada.

La mejor automatización es la que no notas. Esta cumple: he dejado de pensar en “no se me puede olvidar apuntar esto” porque el sistema se acuerda por mí.

Preguntas frecuentes

¿Por qué usar GitHub Issues como bandeja de ideas y no una app de notas?

Porque el blog ya vive en GitHub: los posts son archivos del repositorio. Tener las ideas al lado del contenido las mantiene en el mismo flujo de trabajo, sin herramientas extra que mantener ni sincronizaciones entre apps. Una idea es una issue, y escribir el post la cierra.

¿Cómo se evita que la misma idea genere issues duplicadas?

Con deduplicación por título. Antes de crear nada, el script lee todas las issues con la etiqueta draft, abiertas y cerradas, y salta cualquier idea cuyo título ya exista. Que cuente también las cerradas evita que una idea ya escrita reaparezca como nueva.

¿Hace falta permiso de escritura en YouTube para esto?

Sí, si quieres que el vídeo se borre de la lista tras procesarlo. El scope de solo lectura permite leer la lista, pero para eliminar elementos necesitas el scope de escritura de la cuenta de YouTube. Es lo que da paridad con las tareas de Google, que sí se pueden marcar como completadas.

¿No es poco fiable el cron de GitHub Actions para esto?

Para publicar a una hora exacta, sí, es un problema. Para una bandeja de ideas, no: da igual que una idea tarde una hora o tres en convertirse en issue, porque no la vas a mirar hasta días después. La fiabilidad que necesitas depende del uso.

Fuentes


Compártelo si te ha resultado útil.

Si en tu empresa tenéis ideas, tickets o datos repartidos en herramientas que no se hablan, eso se conecta: cuéntame cómo trabajáis.

¿Dónde apuntas tú las ideas que no quieres perder, y cuántas se te escapan igualmente? Cuéntame.

Y… ahora a vaciar la bandeja, que cuarenta ideas no se escriben solas.

Artículos relacionados

Múltiples agentes de Inteligencia Artificial cargando módulos de conocimiento especializado desde una biblioteca digital

Agent Skills: el estándar que enseña a tus agentes cómo trabajar

Agent Skills es el formato abierto que permite a cualquier agente de Inteligencia Artificial cargar conocimiento especializado bajo demanda: desde cómo escribir en tu blog hasta cómo cerrar un artículo con el CTA correcto. Un skill es simplemente una carpeta con un fichero SKILL.md, pero la idea detrás es poderosa: separar el conocimiento del agente de la herramienta que lo ejecuta. Adoptado por más de treinta herramientas (incluyendo Claude Code, Cursor, GitHub Copilot y Gemini CLI) está convirtiéndose en el estándar de facto. En este post explico cómo funciona, presento el ecosistema en agentskills.io y el directorio de skills.sh, y cuento cómo llevo meses usándolo en este blog sin saber que tenía nombre. Incluyo ejemplos reales del sistema de skills del blog: desde subskills jerárquicos de copywriting hasta un skill de captación de clientes sin una sola línea de código.

Código Python de scraping recolocándose sobre una estructura web que cambia

Scrapling: el scraper de Python que se repara cuando la web cambia

Scrapling es una librería de scraping para Python con una idea que llevábamos años pidiendo: cuando la web cambia su HTML, el scraper se recoloca solo en vez de romperse. Tres fetchers, bypass de Cloudflare, un parser 784 veces más rápido que BeautifulSoup y un framework de spiders incluido. Te cuento qué hace bien, dónde tiene truco y cuándo NO deberías usarlo.

08:30 7 min Marcos Ramírez Lucía