I Just Wanted a WhatsApp Channel That Teaches Me One German Word a Day. It Took a Week and a Git History Rewrite.
Here's the thing nobody tells you about WhatsApp automation: the hard part was never the AI, and it was never the German. It was WhatsApp itself. I wanted something dumb simple - a WhatsApp Channel that posts one German word every morning, A1 level, with an example sentence and a mnemonic. Gemini can write that in a second. The actual project turned out to be an argument with WhatsApp's session model, two hosting platforms, and, at one point, my own repo's git history. The 43MB problem whatsapp-web.js is the library most people reach for first. It drives a real headless Chrome instance that logs into web.whatsapp.com like your browser would, and it works - locally. The moment you try to deploy it, you hit the same wall everyone hits: the session it needs to stay logged in is a full Chromium profile. Cookies, IndexedDB, cache, the works. Mine was 43MB. GitHub Secrets cap out in the kilobytes. So the "obvious" fix - the one a dozen tutorials suggest - is: tar it, base64-encode it, shove it into a secret anyway, decode it back into place on every CI run. I actually did this. I had a script called setup.ts whose entire job was printing a giant base64 blob for me to paste into GitHub's secret editor by hand. It's exactly as bad as it sounds, and it doesn't even solve the real problem, because that session expires. Not on a schedule you control — whenever WhatsApp feels like it. Phone offline too long, and it's gone. Which means eventually you're back to scanning a QR code, except now you're doing it on a server that has no screen. Where do you even scan the QR on a headless box? This is the question I actually came in asking, because I genuinely didn't know. Turns out there are two real answers: Run the linking step once, locally, pointed at the same database your deployed job uses. Scan it on your own laptop, and the session persists wherever you told it to. Or just watch your CI provider's live log stream — the QR prints as ASCII art, and you can scan a phone camera off a terminal on a screen, no problem. Neither needs a screen physically attached to the server. I'd been overcomplicating this in my head for no reason. Railway said no, and I didn't know why yet I had this deployed in three places at once - Railway, Render, GitHub — and none of them worked, and I couldn't figure out why. Turns out Railway's fair-use policy explicitly bans "userbots": anything that logs into a personal account via an unofficial, reverse-engineered protocol instead of a real bot API. whatsapp-web.js is precisely that. It's not a bug I could fix. It's a platform I had to stop using for this. Render fared a little better, but I kept hitting a different, more annoying issue: RemoteAuth with a Mongo-backed session store has open, unresolved GitHub issues where the session saves fine but doesn't reliably restore — silently forcing a fresh QR scan on restart. Not something I broke. Just a library with rough edges. Baileys, and why it's what most of these channels actually run on The fix that actually stuck was switching libraries entirely, to Baileys. It speaks WhatsApp's multi-device protocol directly over a WebSocket. No Chromium, no Puppeteer, nothing to render. The session is a handful of small JSON credential files — kilobytes, not tens of megabytes. That one change collapsed two separate problems at once. The session fits anywhere now — I put it in a free MongoDB Atlas cluster instead of a secret. And without a browser to spin up, the whole thing runs fine on a constrained free-tier box instead of choking on headless-Chrome memory pressure. It also made the hosting question moot. My actual requirement was "post one message a day," which doesn't need an always-on server at all — it needs about sixty seconds of compute, once every 24 hours. That's a GitHub Actions cron job, for free. I deleted the Dockerfile, deleted Render, deleted Railway, and the whole deployment surface became one YAML file with a cron: line in it. The security scare I almost shipped Somewhere in the earlier, messier phase, I had committed a full .wwebjs_auth session folder straight into git — 204 files - plus a couple of standalone tar/base64 dumps of the same thing, at 27MB and 41MB. That's not hypothetical risk. That folder is an active login session. Anyone who could read that repo could have hijacked my WhatsApp account with it. Deleting the files from the working tree isn't enough — they're still sitting in every earlier commit, recoverable by anyone who clones the repo. Fixing it for real meant git filter-repo to strip those paths out of the entire history, followed by a force-push. My .git folder went from 84MB to 284KB in about a second. If you've ever committed something you regret, this is the actual fix — not git rm, the history itself has to go. I only caught this because I asked for a full audit before making the repo public. Worth doing that even if you're sure you didn't leak anything. I was sure. I was wrong. The model chain Google deprecates old models and its numbers so fast the issue isn't "use the newest model," it's "never hardcode one." I wrote a small fallback chain — try the latest Flash model, and on a 404, 429, or 503, fall through to the next one down the list automatically. I know it works because I watched it happen live: the newest model 3.7 hit a genuine 503 Service Unavailable mid-run, fell through to the one behind it, and posted anyway without me touching anything. What's actually running now One GitHub Actions workflow, triggered on a cron schedule. It pulls a WhatsApp session out of a free MongoDB cluster, asks Gemini for a word it hasn't used yet, posts it to the channel, and writes the updated state back to Mongo. No server. No Docker image. No monthly bill. The entire compute cost is about a minute of free CI time a day. The message itself ended up plainer than I expected too — I started with a version stuffed with emoji and flag icons and a line advertising DM commands, and once I actually deleted the interactive bot half of the project, that line was just false advertising to a channel with nobody listening on the other end. It reads like a dictionary entry now. Word, pronunciation, meaning, an example sentence, a note. That turned out to be the right call anyway — it's a vocabulary channel, not a chat app. If you're building anything that automates a personal WhatsApp account: expect the session/auth layer to be the actual project, and the thing you originally wanted to build to be the easy 10%. Checkout the WhatsApp channel and my Github repo Thanks for reading!
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to