Feature Spotlight: AI‑Powered Content Drafting and Publishing
The Problem
Indie developers and small teams ship code faster than they write about it. Every commit that adds a feature, fixes a bug, or refactors a module could be a hook for a blog post or a tweet, but the effort to draft, approve, and schedule that content is a bottleneck. Without a dedicated marketer, teams waste hours on copywriting, formatting, and publishing, and often miss the window when a release is most relevant.
Architecture Overview
Herald solves this by combining
- FastAPI for a lightweight, async API surface,
- SQLAlchemy to persist repository metadata and draft drafts,
- Celery with a Redis broker to run background jobs for content generation and publication,
- React + Vite + Tailwind CSS for a responsive UI that shows the content calendar and draft status.
The core workflow is:
- A GitHub webhook pushes a
pushevent. - FastAPI receives the payload, stores it, and enqueues a Celery task.
- The task uses a LLM provider (OpenAI, Anthropic, etc.) to generate draft copy.
- The draft is stored in PostgreSQL and surfaced to the developer.
- The developer reviews and edits the draft in the browser.
- Once approved, another Celery task publishes to the selected platforms via their APIs (Dev.to, Medium, etc.).
- Analytics are collected to feed back into the content calendar.
Detailed Workflow
1. Webhook Receiver
# app/main.py
from fastapi import FastAPI, Request
from models import Repository, Commit
from tasks import enqueue_content_generation
app = FastAPI()
@app.post("/webhook/github")
async def github_webhook(req: Request):
payload = await req.json()
repo = await Repository.get_or_create(payload["repository"]["full_name"])
commit = Commit.create(repo_id=repo.id, sha=payload["after"], message=payload["head_commit"]["message"])
enqueue_content_generation.delay(commit.id)
return {"status": "queued"}
The endpoint validates the payload, persists the commit, and triggers a background job.
2. Content Generation Task
# app/tasks.py
from celery import shared_task
from llm import generate_draft
from models import Commit, Draft
@shared_task
def enqueue_content_generation(commit_id):
commit = Commit.get(id=commit_id)
prompt = f"Write a concise blog intro for the commit: {commit.message}\n\n---\n" + commit.message
content = generate_draft(prompt)
Draft.create(commit_id=commit.id, body=content, status="draft")
generate_draft calls the chosen LLM via a provider‑agnostic wrapper. The prompt is tuned to produce a short, engaging introduction that can be extended into a full post.
3. Frontend Draft Review
// src/components/DraftEditor.tsx
import React, { useState, useEffect } from "react"
import axios from "axios"
export default function DraftEditor({ draftId }: { draftId: number }) {
const [body, setBody] = useState<string>('')
useEffect(() => {
axios.get(`/api/drafts/${draftId}`).then(r => setBody(r.data.body))
}, [draftId])
const save = () => {
axios.put(`/api/drafts/${draftId}`, { body })
}
return (
<textarea className="w-full h-64 p-2 border rounded" value={body} onChange={e => setBody(e.target.value)} />
)
}
The UI is built with React and styled using Tailwind CSS. A simple textarea lets the developer tweak the LLM output before publishing.
4. Publishing Task
# app/tasks.py
@shared_task
def publish_to_devto(draft_id):
draft = Draft.get(id=draft_id)
headers = {"api-key": os.getenv("DEVTO_API_KEY")}
payload = {
"article": {
"title": draft.title,
"body_markdown": draft.body,
"published": True,
"tags": ["devops", "automation"]
}
}
resp = requests.post("https://dev.to/api/articles", json=payload, headers=headers)
if resp.status_code == 201:
draft.status = "published"
draft.published_at = datetime.utcnow()
draft.save()
The task uses the Dev.to API to publish the article. Similar tasks exist for Medium and other platforms.
Performance & Reliability
-
Composite Index: A composite index on
(commit_id, status)speeds up queries for the latest draft per commit. -
Query Projection: The
overview()endpoint now selects only the columns needed for the content calendar, reducing payload size. - Error Handling: Improved error messages now include depth and size limits, guiding developers to fix payloads before resubmission.
-
Retry Guard:
retry_publicationis protected against re‑running when a publication is already in thePUBLISHINGstate, preventing duplicate posts.
User Experience
On the dashboard, the content calendar shows each commit as a card with status badges: Draft, Approved, Publishing, Published. Clicking a card opens the DraftEditor component. After approval, the user clicks “Publish”, which triggers the Celery task. Once published, a link to the live article appears, and analytics counters update in real time.
Conclusion
Herald’s AI‑powered drafting feature turns every meaningful commit into a publishable piece without manual copywriting. By leveraging FastAPI, Celery, and the Dev.to API, it delivers a seamless marketing automation flow that fits naturally into a developer’s workflow.
This feature spotlight is based on the latest commit history of the Herald repository.
Top comments (5)
nice bro
very good
verification
verification account
You need to verify your account.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.