Initial commit: AutoBlogger v0.1.0

WordPress plugin that auto-researches topics from Reddit, generates
SEO-optimized articles via OpenRouter LLMs, runs them through a chief
editor agent, and publishes on a daily schedule.

Includes:
- Full generation pipeline (collect → analyze → score → generate → review → publish)
- Cost tracking and budget enforcement
- Admin UI: settings, review queue, metrics, activity log
- CI/CD pipeline (GitHub Actions → WordPress REST deploy)
- Unit test suite (PHPUnit + Brain Monkey)
- Structured logging with configurable verbosity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Terence Ong
2026-04-10 16:10:27 +07:00
commit 68c6f67bcf
65 changed files with 8833 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
<?php
declare(strict_types=1);
/**
* Value object representing a single piece of collected social media data
* (a Reddit post, a TikTok comment, etc.).
*
* Triggered by: Source providers create these; Content_Analyzer consumes them.
* Dependencies: None — pure data object.
*
* @see providers/interface-source-provider.php — Providers return arrays of these.
* @see core/class-content-analyzer.php — Consumes these for pattern detection.
*/
class Autoblogger_Source_Data {
private int $id;
private string $source_type;
private string $source_id;
private ?string $subreddit;
private ?string $title;
private ?string $content;
private ?string $author;
private int $score;
private int $comment_count;
private ?string $permalink;
private string $collected_at;
private ?array $metadata;
/**
* @param array{
* id?: int,
* source_type: string,
* source_id: string,
* subreddit?: string|null,
* title?: string|null,
* content?: string|null,
* author?: string|null,
* score?: int,
* comment_count?: int,
* permalink?: string|null,
* collected_at?: string,
* metadata?: array|null,
* } $data
*/
public function __construct( array $data ) {
$this->id = (int) ( $data['id'] ?? 0 );
$this->source_type = $data['source_type'];
$this->source_id = $data['source_id'];
$this->subreddit = $data['subreddit'] ?? null;
$this->title = $data['title'] ?? null;
$this->content = $data['content'] ?? null;
$this->author = $data['author'] ?? null;
$this->score = (int) ( $data['score'] ?? 0 );
$this->comment_count = (int) ( $data['comment_count'] ?? 0 );
$this->permalink = $data['permalink'] ?? null;
$this->collected_at = $data['collected_at'] ?? current_time( 'mysql' );
$this->metadata = $data['metadata'] ?? null;
}
public function get_id(): int {
return $this->id;
}
public function get_source_type(): string {
return $this->source_type;
}
public function get_source_id(): string {
return $this->source_id;
}
public function get_subreddit(): ?string {
return $this->subreddit;
}
public function get_title(): ?string {
return $this->title;
}
public function get_content(): ?string {
return $this->content;
}
public function get_author(): ?string {
return $this->author;
}
public function get_score(): int {
return $this->score;
}
public function get_comment_count(): int {
return $this->comment_count;
}
public function get_permalink(): ?string {
return $this->permalink;
}
public function get_collected_at(): string {
return $this->collected_at;
}
public function get_metadata(): ?array {
return $this->metadata;
}
/**
* Convert to array for database insertion.
*
* @return array<string, mixed>
*/
public function to_db_row(): array {
return [
'source_type' => $this->source_type,
'source_id' => $this->source_id,
'subreddit' => $this->subreddit,
'title' => $this->title,
'content' => $this->content,
'author' => $this->author,
'score' => $this->score,
'comment_count' => $this->comment_count,
'permalink' => $this->permalink,
'collected_at' => $this->collected_at,
'metadata_json' => null !== $this->metadata ? wp_json_encode( $this->metadata ) : null,
];
}
}