Skip to content

Repository files navigation

joomla-ai-autoposter

One-command autoposting pipeline for a Joomla 3.x blog powered by any OpenAI-compatible LLM (DeepSeek, OpenAI, OpenRouter, local Ollama…).

It picks the next topic, writes an SEO article through an LLM, validates the HTML deterministically, publishes it to Joomla via a real browser (Playwright) and submits the URL for re-crawling to Yandex.Webmaster — all in one run.

Русская версия: README.ru.md


Pipeline architecture


What problem it solves

Filling a blog manually is monotonous: think of a topic, write the text, format it in the admin panel, publish, then request re-indexing. This project turns it into a supervised pipeline:

topic → LLM article → HTML validation → Joomla (Playwright) → log → re-crawl

It was built for a real workflow of two articles per day on an old Joomla 3.10 site. The LLM is used only to write the article — CSV handling, file management, HTML checks, the browser and the API are plain code, which keeps token usage low and makes every step deterministic.

Features

  • One commandpython run.py runs the whole cycle.
  • Topic management — reads headlines.csv, advances statuses (не использован → в работе → черновик → опубликован).
  • LLM copywriter — strict prompt, returns only HTML (no markdown).
  • Deterministic validation — catches service-junk markers ([citation], , ), markdown, HTML comments, wrong wrappers, missing <hr id="system-readmore" />, link allowlist and text length; one repair pass is attempted before failing.
  • Joomla publisher — Playwright: logs in, duplicates the latest article via Save as Copy (inheriting all hidden fields), fills Title / Alias / CodeMirror content, forces Published and saves.
  • Re-crawl — submits the URL to Yandex.Webmaster through the official REST API (no browser login to a sensitive service).
  • Resumable — a failed publication stays in черновик and can be retried with --publish-draft; drafts and logs live on disk.
  • Safe by default — secrets and the browser profile are kept out of the repository (see .gitignore).

Two ways to run it

  1. One-command Python pipeline (run.py) — the recommended, token-cheap production mode described below.
  2. Three Cherry Studio assistants + MCP — the original architecture from the case study. Ready-made system prompts are in prompts/: Headlines, Copywriter and Posting, connected through Desktop Commander and Playwright MCP servers. Useful when you want a human check between stages.

Requirements

  • Python 3.10+
  • A Joomla 3.x site with an admin panel
  • An OpenAI-compatible LLM endpoint + API key
  • (Optional) Yandex.Webmaster with a verified host + OAuth token
  • Chromium for Playwright (python -m playwright install chromium)

Quick start

# 1. Clone and install
git clone https://github.com/YanChi-pixel/joomla-ai-autoposter.git
cd joomla-ai-autoposter
python -m venv .venv
.venv\Scripts\activate          # Windows
# source .venv/bin/activate     # macOS / Linux
pip install -r requirements.txt
python -m playwright install chromium

# 2. Configure
copy config.example.json config.json     # Windows
# cp config.example.json config.json     # macOS / Linux
# …then edit config.json and fill in your values

# 3. Prepare the secrets folder
mkdir .secrets
# Put: llm-api-key.txt, joomla-credentials.txt (login\npassword), yandex-token.txt

# 4. Prepare the topic list
copy examples\headlines.example.csv headlines.csv

# 5. Test generation only (no publishing)
python run.py --test

# 6. Full cycle
python run.py

Configuration

config.json is the only settings file. Relative paths are resolved from the project root, so the repository can be cloned anywhere.

Section Key Meaning
paths headlines topic list CSV (default headlines.csv)
paths current_task hand-off file (default current-task.txt)
paths published_log publication log CSV
paths drafts drafts directory
paths recrawl_token Yandex token file (default .secrets/yandex-token.txt)
llm base_url OpenAI-compatible endpoint
llm model model name (default deepseek-chat)
llm api_key_file API key file (default .secrets/llm-api-key.txt)
llm temperature / max_tokens generation params
joomla base_url admin URL (https://site.example.com/administrator/)
joomla login_url admin login URL
joomla credentials_file login/password file
joomla profile_dir persistent Playwright profile directory
joomla headless false for the first run, then true
joomla public_blog_path public blog URL used to build article links
recrawl host verified host in Yandex.Webmaster
seo links internal links the LLM may use (keywords/title/href/anchor)

Usage

python run.py                    # full cycle
python run.py --test             # generate only, no publishing, no CSV changes
python run.py next               # show the next free topic
python run.py --title "Topic"    # generate an article for a custom topic
python run.py --publish-draft    # publish an existing draft

Topic statuses in headlines.csv:

не использован  →  в работе  →  черновик  →  опубликован
                     │
                     └── ошибка

Getting the secrets

LLM API key

Put the key into .secrets/llm-api-key.txt (one line) or set the LLM_API_KEY / DEEPSEEK_API_KEY environment variable.

Joomla credentials

.secrets/joomla-credentials.txt — first line login, second line password.

Yandex.Webmaster token

  1. Create an OAuth application in Yandex and get a token (valid ~6 months).
  2. Save it to .secrets/yandex-token.txt.
  3. Make sure the site host is verified in Webmaster.

Manual re-crawl:

python recrawl.py --host your-site.example.com "https://your-site.example.com/blog/123-post.html"

How the Joomla publisher works

The content field in Joomla is a CodeMirror editor, not a plain textarea, so fill() is unreliable. The publisher:

  1. opens the admin and logs in if needed;
  2. opens the latest article and clicks Save as Copy (this inherits hidden fields like the "Read More" text);
  3. fills #jform_title, #jform_alias and the CodeMirror content via the editor's own API (cm.CodeMirror.setValue(...) + save());
  4. forces #jform_state to 1 (Published);
  5. clicks Save & Close and verifies the article list appeared.

First run: keep "headless": false and log in manually once if the auto-login does not work — the persistent profile_dir remembers the session.

Troubleshooting

  • LLM API key not found — fill .secrets/llm-api-key.txt or set the env var.
  • playwright is not installedpip install playwright and python -m playwright install chromium.
  • Could not log into Joomla — check the credentials, log in manually once.
  • Failed to insert the full HTML — the editor is not CodeMirror or the pause is too short; adjust _set_content in browser/publish.py.
  • HTTP 429 from Yandex — the daily re-crawl quota is exhausted; retry tomorrow.
  • Console shows ������ — run chcp 65001 before the command.
  • Yandex token expired — refresh .secrets/yandex-token.txt.

Project structure

joomla-ai-autoposter/
├── run.py                    # one-command orchestrator
├── recrawl.py                # Yandex.Webmaster re-crawl
├── article_prompt.txt        # universal article prompt
├── browser/
│   └── publish.py            # Playwright Joomla publisher
├── prompts/                  # Cherry Studio system prompts (3-assistant flow)
├── examples/                 # example CSV files
├── docs/                     # architecture diagram + generator
├── config.example.json
├── .github/workflows/ci.yml
├── CHANGELOG.md
└── requirements.txt

Safety checklist

  • Run --test before the first production run.
  • Never commit .secrets/, playwright-profile/, drafts/, headlines.csv or published-log/ — they are ignored via .gitignore.
  • Review the draft before publishing at scale.

License

MIT

Disclaimer

This tool writes to a production Joomla site through browser automation and calls the Yandex.Webmaster API. Test on a staging copy first, respect rate limits, and review the LLM-generated content before publishing at scale.

About

One-command Joomla autoposting pipeline: LLM article → validation → Playwright publishing → Yandex.Webmaster re-crawl

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages