Saltar al contenido
Development

Cómo construí un backfill de sindicación del blog

Por Victor Da Luz
railsrubymediumdev-logblog-manager

He estado construyendo una app Rails, blog-manager, para rastrear y sindicar publicaciones entre vdaluz.com, Medium y LinkedIn. Después de lograr que funcionara la integración con Medium, me topé con un problema práctico: ya existían 72 publicaciones en Medium de las que la base de datos local no sabía nada. Todo tenía medium_status: not_imported. Antes de escribir código nuevo de publicación, necesitaba sincronizar el estado local.

El callejón sin salida del RSS

El primer intento usó el feed RSS en medium.com/feed/@vdaluz. El servicio Medium::PublishedBackfill lo consultaba y emparejaba publicaciones por URL o por título. Funcionaba, pero solo devolvía 10 publicaciones. Medium limita el RSS público a 10 elementos. Había 72.

Yendo más profundo con GraphQL

Medium tiene una API GraphQL no documentada en medium.com/_/graphql. Las sesiones con inicio de sesión pueden consultar latestPostsConnection(type: POST_TYPE_PUBLIC), paginada mediante cursor. Usé una superficie de navegador ya con sesión iniciada en Medium para correr consultas autenticadas y recorrer las 72 publicaciones en tres solicitudes (25+25+22).

La respuesta devolvía el id, title, firstPublishedAt y previewImage.id de cada publicación, exactamente lo necesario para el backfill.

Coincidencia de títulos y trampas de Unicode

Emparejé publicaciones con registros locales en dos niveles: primero por medium_url (publicaciones ya vinculadas), después por título normalizado. El segundo nivel topó con un problema inesperado: Medium guarda los apóstrofes como comillas curvas Unicode (U+2019), mientras que el frontmatter en markdown usa apóstrofes ASCII rectos (U+0027). Títulos como “What’s Next” con comilla curva no coincidían con “What’s Next” con una recta.

La solución fue una función de normalización que convierte las comillas curvas en rectas antes de comparar. Después de eso, 69 de las 72 publicaciones coincidieron sin problema. Los 3 casos fallidos eran publicaciones muy antiguas con títulos sustancialmente distintos entre plataformas.

La trampa del stdin de kamal

Escribí un script de Rails runner para hacer las actualizaciones e intenté correrlo con bin/kamal app exec --reuse "bin/rails runner -" < script.rb. Salió con código 0, tardó unos 6 segundos, y no hizo absolutamente nada. El script corrió pero con stdin vacío, el app exec de kamal no reenvía el stdin del shell que lo invoca hacia el contenedor.

La solución: hacer scp del script al host, docker cp hacia el contenedor, y después docker exec directamente. Obvio en retrospectiva, molesto de descubrir.

Imágenes destacadas en vdaluz.com

Con hero_image_url completo para 69 publicaciones (URLs del CDN de Medium), el siguiente paso era llevar esas imágenes al sitio Astro de vdaluz.com. Descargué las 82 imágenes (72 publicadas + 14 programadas, menos 4 superposiciones), generé variantes .webp, agregué el frontmatter heroImage a los archivos markdown correspondientes, y actualicé tres plantillas de Astro para mostrarlas: banner a todo lo ancho en las páginas de publicación, miniatura en las tarjetas de listado, miniatura en las tarjetas de lectura relacionada.

El campo heroImage ya estaba conectado a las etiquetas meta de OG. Simplemente no se estaba mostrando visualmente en ningún lado. Esa fue una observación de una línea que terminó en una actualización de tres plantillas.

Lo que hay en la base de datos ahora

Después del backfill: 69 publicaciones con medium_status: published, 14 con medium_status: scheduled (extraídas de la interfaz de Medium), 2 sin coincidencia (discrepancias de título antiguas). El servicio DraftCreator ya lee hero_image_url al construir borradores de Medium, así que las publicaciones nuevas incluirán su imagen destacada automáticamente en cuanto esa columna esté completa.

Lecturas relacionadas