I continue my vibe coding escapade with my first bot: a song-of-the-day Telegram bot that automatically sends a tasty track to subscribers every morning to enjoy with coffee.
Before any bot building, I curated a set of my favorite songs of all time—a revelatory exercise that clarified my tastes: lots of classic rock and folk-rock from the ’60s-‘80s golden era, plus generous helpings of singer-songwriters, soul/R&B, and live tracks. Christmas music (both sacred and secular) bookends the calendar from early December through early January, confirming what I previously discussed: I do love the season. I aimed for artist diversity (no more than six tracks each) and gravitate toward deep cuts—so if you recognize a hit here, it’s because it’s genuinely great. But as Grandpa Brennan was wont to say, de gustibus non est disputandum.
This was a product of Claude (Sonnet 4.5). Admittedly, it didn’t get everything perfect on the first pass, but it sure took my abrasive feedback in stride. The process wasn’t totally linear, but it went something like this:
Fail, fail, fail, success, fail, fail...
Here’s what I ended up with:
_data/daily_song.yml) – My impeccably-curated list of 366 songs2 with date, track title, artist, and YouTube Music song ID.
- date: "12-09"
track: "Christmas Is Coming"
artist: "Vince Guaraldi Trio"
songId: "2--a88MKHZc"
telegram-subscriptions.yml) – Runs daily at 07:00 Pacific, and checks for /subscribe and /unsubscribe messages, adding/removing users to/from _data/telegram_subscribers.json, kicks out confirmation messages.telegram-daily-song.yml) – Runs a half hour later (~07:45), reading today’s song from YAML, sending the selection with clickable link to the track on YouTube Music to all subscribers in the JSON file:
/unsubscribe if you’re not feelin’ my jams.).yml files from my GitHub repo. I’ll subscribe.It’s astonishing that a non-coder like moi can go from idea to fully functional Telegram bot with auto-subscriptions in one sub-100-message chat session—that there’s some right-fine vibe codin’…at least for the standards of late 2025 (I’m sure it’ll seem quaint in a year or two).
Six months in, two problems surfaced: the song was arriving noticeably late and at inconsistent times. Turned out there were two culprits conspiring together: 1) GitHub’s automation scheduler having no awareness of local time or seasonal clock changes, and 2) GitHub’s scheduler not being very punctual, its automations more like suggestions than alarms (ergo day-to-day randomness). So I swapped GitHub’s internal scheduler for cron-job.org, a dedicated (and free) scheduling service that pings GitHub at exactly the right local time each day (even accounting for DST) and says “run the song workflow now, Bro”. Delivery is now consistent to within a minute. The whole diagnosis-and-fix took ~20 minutes with Claude (Sonnet 4.6).
While at it, added an X companion account—@pb_song_bot—so the daily song now posts there, too. Getting the X API to cooperate required a small credits deposit (the “free” tier is free like a bookie extending credit: new guys post cash first), but once unlocked, the same cron-job.org trigger fires both automations in perfect synchrony.
#128: "I Just Don't Think I'll Ever Get Over You" by Colin Hayhttps://t.co/ZW3scbQWoN
— Paul's Song of the Day (@pb_song_bot) May 8, 2026
— ᴘ. ᴍ. ʙ.
This is the type of arcana that’s over my head and so I’m totally reliant on the LLM to diagnose and fix: Node.js was trying to run our script as an ES Module (basically new JavaScript) (which doesn’t allow require()), but the thing was written in old Javascript (i.e., CommonJS style), so got this error about “require is not defined in ES module scope.” So the fix—after a number of back-and-forths—was renaming the file to .cjs (CommonJS file extension) and wrapped the await in an async function, which told Node: “Hey, this is old-style CommonJS, not modern ES Modules.” 🤓 ↩
Yes, leap year support built in. ↩