Crypto Articles Queue: a day's publishing in one click
I publish Arabic crypto news to sites I don’t own. That used to be most of a day, every day: read the English feeds, decide what’s worth covering, rewrite it in Arabic, find a picture I’m allowed to publish, post it to every site, and track what already went out so nothing ships twice. Crypto Articles Queue does that day in one click — and keeps, on purpose, the parts a machine shouldn’t own.
what it does
One button, once a day. It pulls the day’s candidates from English crypto feeds, turns each story into a finished Arabic article written for search, gives it an image, and publishes across Blogger and WordPress — dealing the day’s work out so no site floods and none gets skipped.
There are two ways it stops, and the line between them is the design. Anything it can verify went wrong holds the article where it is and raises it for me: a feed that won’t parse, a model that won’t answer, a story with no image I’m licensed to publish. Nothing is lost and nothing goes out half-made — the article waits in the queue and the next run picks it up.
Then there’s what it can’t verify. No check I can write catches Arabic that reads fluently and uses a word that doesn’t exist, so the pipeline doesn’t pretend to have one. Publishing is unattended, and the backstop is me reading live output on a schedule. That’s a deliberate trade: at this volume and this kind of content, throughput beats per-item precision. For client-facing work it would be the wrong call and the gate goes back in.
A day’s work, every day, became one click and an hour of review.
the language pair
Source locale English, target locale Arabic. That pair is the actual job; everything else is plumbing around it.
It isn’t translation, it’s a rewrite with a contract. The prompt doesn’t return prose, it returns a publishable object: the Arabic body as clean HTML, a focus keyword, a meta description, a separate Arabic SEO title, and — in English — the search term for the picture. Register and terminology are pinned in the prompt so a coin carries the same Arabic name today that it carried last month, and so the body arrives ready to hand to a CMS without me touching it.
Arabic SEO is not English SEO translated. The keyword someone actually types is Arabic, and so is the headline that has to earn the click. The stock-photo query has to be English, because the image libraries have no Arabic index. One pass therefore produces both locales for two different consumers — the reader and the asset search — and has to know which is which. Those fields land in the site’s SEO plugin as post meta at publish time, which is the step where a translated article becomes a page that can be found.
Internal linking is a termbase. A curated map of terms to URLs, with approved variants per term: the English name, the old name a story might still use, the ticker, the form people actually type. Only the first mention in a body gets linked, headings and existing links are off-limits, and ambiguous terms are blocklisted by hand so a three-letter ticker can’t swallow an ordinary word. Every rule in there exists because the naive version got something wrong in public.
It runs continuously, not in batches. New source content triggers the pipeline — no project, no handoff, no delivery date. The industry name for that pattern is continuous localization, which I’d have called “a script on a timer” before I found out there was a word for it.
what it took
The happy path was the small part. The build is in everything that goes wrong.
- Publishing where the API says no. One platform refuses write access to my account outright — not a bug I could fix, a policy I couldn’t argue with. So the tool drives the real editor in a browser and posts the way I would by hand. In localization terms that’s connector development: building the integration for a target platform that doesn’t ship one. A published post is a published post; when the front door is locked you find another one.
- Not publishing the same thing twice. Two different problems wear that one word. The first is re-covering a story I’ve already run, and it’s settled cheaply — exact URL matching against a permanent ledger plus a freshness window, so nothing re-enters the queue. I deliberately didn’t build fuzzy same-story matching: when two outlets cover one event I let both through, because a false merge drops a story silently and I’d never find out. The second problem is the expensive one. The same Arabic body on two sites is duplicate content, and it works against the exact search visibility the pipeline exists to build — so each article is dealt exactly one destination and never cross-posted.
- Images I’m allowed to use. On topic, licensed for what I’m doing with it, and never the same picture twice. Automated publishing under a licensing bar means the constraint has to live in the code: only sources that are free to publish with no attribution, bytes re-hosted rather than hotlinked, cached for as long as the terms require, and a hard rule that an article without a usable image doesn’t go out picture-less — it waits for me to give it one. The tempting shortcut is lifting the source article’s own photo, which is someone else’s licensed editorial image and the exact thing this approach exists to avoid.
- Making mistakes cheap. Everything that can touch a live site is guarded, and the guards were written the day a test run went out live. Anything that publishes earns a rejection path before it earns a feature.
- Surviving unattended. A dead feed, a model that won’t answer, a page that quietly changed its layout — none of them get to take the day down. Failures get classified before they get handled: a rate limit, a revoked credential, a regional block and a plain network problem look identical from the outside, and treating one as another has you replacing keys that were never broken. The day’s counts are read back from the published record rather than tallied in memory, so relaunching a run mid-afternoon can’t double-publish. The whole cycle is one headless command, so moving it onto a schedule somewhere permanent is a config change, not a rewrite.
the takeaway
The thing I’m taking to the next build: an automation is only worth having if you can walk away from it. That trust isn’t earned on the happy path. It’s earned by what happens when something is missing, ambiguous, or offline — and by being honest about which decisions shouldn’t be automated at all. The review queue isn’t a hole in the automation. It’s what makes the rest of it safe to leave alone.
Nine thousand lines of TypeScript. I built it with Claude Code doing the typing; the design, the decisions, and every failure it hit in production were mine.