style(phpcs): autofix WordPress-Core violations via phpcbf

Run phpcbf --standard=WordPress-Core across includes/, prautoblogger.php,
and uninstall.php. Fixed 1,362 formatting violations in 75 files:
  - Short array syntax: [] → array()
  - Line breaking for long function calls
  - Consistent indentation
  - Function call parameter alignment

No logic changes; purely mechanical formatting.
This commit is contained in:
peptiderepo
2026-04-23 02:42:48 +00:00
parent 7cd65b145f
commit f3eacd0d8d
75 changed files with 2148 additions and 1412 deletions

View File

@@ -23,14 +23,14 @@ class PRAutoBlogger_Analysis_Prompts {
* @return string System prompt text.
*/
public static function build_system_prompt( string $niche, string $performance_context, int $target_count = 6 ): string {
$prompt = "You are a content strategist analyzing social media discussions to find article ideas for a blog";
$prompt = 'You are a content strategist analyzing social media discussions to find article ideas for a blog';
if ( '' !== $niche ) {
$prompt .= " in the {$niche} niche";
}
$prompt .= ".\n\n";
$prompt .= "IMPORTANT: You must identify exactly {$target_count} DISTINCT article ideas. ";
$prompt .= "Each idea must cover a substantially different topic — no two ideas should overlap ";
$prompt .= 'Each idea must cover a substantially different topic — no two ideas should overlap ';
$prompt .= "in their main subject. Aim for diversity across these categories:\n";
$prompt .= "1. QUESTIONS: Recurring questions people ask (\"How do I...\", \"What is...\", \"Is it safe to...\")\n";
$prompt .= "2. COMPLAINTS: Pain points, frustrations, or problems people report\n";
@@ -39,7 +39,7 @@ class PRAutoBlogger_Analysis_Prompts {
$prompt .= "5. GUIDES: How-to topics, best practices, or educational content people need\n\n";
$prompt .= "Spread your {$target_count} ideas across multiple categories. ";
$prompt .= "Be specific — \"peptide dosing for BPC-157\" is better than \"peptide information\". ";
$prompt .= 'Be specific — "peptide dosing for BPC-157" is better than "peptide information". ';
$prompt .= "Each idea should be narrow enough to be a single focused blog post.\n\n";
$prompt .= "For each idea, provide:\n";
@@ -81,7 +81,7 @@ class PRAutoBlogger_Analysis_Prompts {
* @return string User prompt text.
*/
public static function build_user_prompt( string $summary, int $target_count = 6 ): string {
return "Here are the recent social media posts and comments to analyze. "
return 'Here are the recent social media posts and comments to analyze. '
. "Find {$target_count} distinct, diverse article ideas from this data:\n\n"
. $summary;
}
@@ -112,7 +112,7 @@ class PRAutoBlogger_Analysis_Prompts {
return '';
}
$lines = [ 'Top performing past articles (learn from these):' ];
$lines = array( 'Top performing past articles (learn from these):' );
foreach ( $top_posts as $post ) {
$lines[] = sprintf(
'- "%s" (score: %.1f)',
@@ -136,27 +136,29 @@ class PRAutoBlogger_Analysis_Prompts {
* @return string Formatted context block, or empty string if no recent articles.
*/
public static function get_recent_articles_context(): string {
$query = new \WP_Query( [
'post_type' => 'post',
'post_status' => [ 'publish', 'draft', 'pending' ],
'meta_key' => '_prautoblogger_generated',
'meta_value' => '1',
'date_query' => [ [ 'after' => '30 days ago' ] ],
'posts_per_page' => 100,
'fields' => 'ids',
'no_found_rows' => true,
] );
$query = new \WP_Query(
array(
'post_type' => 'post',
'post_status' => array( 'publish', 'draft', 'pending' ),
'meta_key' => '_prautoblogger_generated',
'meta_value' => '1',
'date_query' => array( array( 'after' => '30 days ago' ) ),
'posts_per_page' => 100,
'fields' => 'ids',
'no_found_rows' => true,
)
);
if ( empty( $query->posts ) ) {
return '';
}
$titles = [];
$titles = array();
foreach ( $query->posts as $post_id ) {
$titles[] = '- "' . get_the_title( $post_id ) . '"';
}
$block = "IMPORTANT — Topics already covered (last 30 days). Do NOT suggest topics that overlap with these. ";
$block = 'IMPORTANT — Topics already covered (last 30 days). Do NOT suggest topics that overlap with these. ';
$block .= "Find genuinely NEW angles, questions, or subjects that are NOT already covered:\n";
$block .= implode( "\n", $titles );

View File

@@ -47,20 +47,20 @@ class PRAutoBlogger_Article_Worker {
$auto_publish = in_array(
get_option( 'prautoblogger_auto_publish', '0' ),
[ '1', 'yes' ],
array( '1', 'yes' ),
true
);
$result = [
$result = array(
'generated' => 0,
'published' => 0,
'rejected' => 0,
'cost' => 0.0,
];
);
try {
$this->broadcast_stage( __( 'Generating article draft via AI…', 'prautoblogger' ) );
$content = $generator->generate( $idea );
$content = $generator->generate( $idea );
$result['generated'] = 1;
$this->broadcast_stage( __( 'Running editorial pass…', 'prautoblogger' ) );
@@ -68,7 +68,12 @@ class PRAutoBlogger_Article_Worker {
$this->broadcast_stage( __( 'Saving and publishing…', 'prautoblogger' ) );
$this->publish_or_draft(
$content, $idea, $review, $publisher, $auto_publish, $result
$content,
$idea,
$review,
$publisher,
$auto_publish,
$result
);
} catch ( \Throwable $e ) {
PRAutoBlogger_Logger::instance()->error(
@@ -114,13 +119,13 @@ class PRAutoBlogger_Article_Worker {
if ( $auto_publish ) {
$publisher->publish( $final, $idea, $review, $run_id );
$result['published']++;
++$result['published'];
} else {
$publisher->save_as_draft( $final, $idea, $review, $run_id );
}
} else {
$publisher->save_as_draft( $content, $idea, $review, $run_id );
$result['rejected']++;
++$result['rejected'];
PRAutoBlogger_Logger::instance()->info(
'Article rejected by editor: ' . $idea->get_topic(),
'pipeline'

View File

@@ -51,16 +51,22 @@ class PRAutoBlogger_Chief_Editor {
$user_prompt = $this->build_review_prompt( $content, $idea );
$response = $this->llm->send_chat_completion(
[
[ 'role' => 'system', 'content' => $system_prompt ],
[ 'role' => 'user', 'content' => $user_prompt ],
],
array(
array(
'role' => 'system',
'content' => $system_prompt,
),
array(
'role' => 'user',
'content' => $user_prompt,
),
),
$model,
[
array(
'temperature' => 0.3,
'max_tokens' => 5000,
'response_format' => [ 'type' => 'json_object' ],
]
'response_format' => array( 'type' => 'json_object' ),
)
);
$this->cost_tracker->log_api_call(
@@ -81,7 +87,7 @@ class PRAutoBlogger_Chief_Editor {
* @return string
*/
private function build_system_prompt( string $niche ): string {
$prompt = "You are a senior blog editor";
$prompt = 'You are a senior blog editor';
if ( '' !== $niche ) {
$prompt .= " specializing in {$niche} content";
}
@@ -161,27 +167,31 @@ class PRAutoBlogger_Chief_Editor {
. mb_substr( $content, 0, 500 ),
'editor'
);
return new PRAutoBlogger_Editorial_Review( [
'verdict' => 'rejected',
'notes' => 'Editor response could not be parsed.',
'quality_score' => 0.0,
'seo_score' => 0.0,
'issues' => [ 'Unparseable editor response' ],
] );
return new PRAutoBlogger_Editorial_Review(
array(
'verdict' => 'rejected',
'notes' => 'Editor response could not be parsed.',
'quality_score' => 0.0,
'seo_score' => 0.0,
'issues' => array( 'Unparseable editor response' ),
)
);
}
$verdict = sanitize_text_field( $data['verdict'] ?? 'rejected' );
if ( ! in_array( $verdict, [ 'approved', 'revised', 'rejected' ], true ) ) {
if ( ! in_array( $verdict, array( 'approved', 'revised', 'rejected' ), true ) ) {
$verdict = 'rejected';
}
return new PRAutoBlogger_Editorial_Review( [
'verdict' => $verdict,
'notes' => sanitize_textarea_field( $data['notes'] ?? '' ),
'revised_content' => isset( $data['revised_content'] ) ? wp_kses_post( $data['revised_content'] ) : null,
'quality_score' => (float) ( $data['quality_score'] ?? 0.0 ),
'seo_score' => (float) ( $data['seo_score'] ?? 0.0 ),
'issues' => array_map( 'sanitize_text_field', $data['issues'] ?? [] ),
] );
return new PRAutoBlogger_Editorial_Review(
array(
'verdict' => $verdict,
'notes' => sanitize_textarea_field( $data['notes'] ?? '' ),
'revised_content' => isset( $data['revised_content'] ) ? wp_kses_post( $data['revised_content'] ) : null,
'quality_score' => (float) ( $data['quality_score'] ?? 0.0 ),
'seo_score' => (float) ( $data['seo_score'] ?? 0.0 ),
'issues' => array_map( 'sanitize_text_field', $data['issues'] ?? array() ),
)
);
}
}

View File

@@ -49,7 +49,7 @@ class PRAutoBlogger_Content_Analyzer {
if ( empty( $source_items ) ) {
PRAutoBlogger_Logger::instance()->info( 'No new source data to analyze.', 'analyzer' );
return [];
return array();
}
$niche = get_option( 'prautoblogger_niche_description', '' );
@@ -61,16 +61,22 @@ class PRAutoBlogger_Content_Analyzer {
$user_prompt = PRAutoBlogger_Analysis_Prompts::build_user_prompt( $summary, $target_idea_count );
$response = $this->llm->send_chat_completion(
[
[ 'role' => 'system', 'content' => $system_prompt ],
[ 'role' => 'user', 'content' => $user_prompt ],
],
array(
array(
'role' => 'system',
'content' => $system_prompt,
),
array(
'role' => 'user',
'content' => $user_prompt,
),
),
$model,
[
array(
'temperature' => 0.5,
'max_tokens' => 8000,
'response_format' => [ 'type' => 'json_object' ],
]
'response_format' => array( 'type' => 'json_object' ),
)
);
$this->cost_tracker->log_api_call(
@@ -130,7 +136,7 @@ class PRAutoBlogger_Content_Analyzer {
gmdate( 'Y-m-d H:i:s', time() - DAY_IN_SECONDS )
),
ARRAY_A
) ?: [];
) ?: array();
}
/**
@@ -142,8 +148,8 @@ class PRAutoBlogger_Content_Analyzer {
* @return string Formatted text summary.
*/
private function build_source_summary( array $items ): string {
$parts = [];
$char_limit = 30000; // Roughly ~7500 tokens.
$parts = array();
$char_limit = 30000; // Roughly ~7500 tokens.
$current_chars = 0;
foreach ( $items as $item ) {
@@ -163,7 +169,7 @@ class PRAutoBlogger_Content_Analyzer {
break;
}
$parts[] = $entry;
$parts[] = $entry;
$current_chars += strlen( $entry );
}
@@ -185,27 +191,29 @@ class PRAutoBlogger_Content_Analyzer {
. mb_substr( $content, 0, 500 ),
'analyzer'
);
return [];
return array();
}
$source_ids = array_column( $source_items, 'id' );
$results = [];
$results = array();
foreach ( $data['patterns'] as $pattern ) {
$results[] = new PRAutoBlogger_Analysis_Result( [
'analysis_type' => sanitize_text_field( $pattern['type'] ?? 'question' ),
'topic' => sanitize_text_field( $pattern['topic'] ?? '' ),
'summary' => sanitize_textarea_field( $pattern['summary'] ?? '' ),
'frequency' => absint( $pattern['frequency'] ?? 1 ),
'relevance_score' => (float) ( $pattern['relevance_score'] ?? 0.0 ),
'source_ids' => $source_ids,
'analyzed_at' => current_time( 'mysql' ),
'metadata' => [
'suggested_title' => $pattern['suggested_title'] ?? '',
'key_points' => $pattern['key_points'] ?? [],
'target_keywords' => $pattern['target_keywords'] ?? [],
],
] );
$results[] = new PRAutoBlogger_Analysis_Result(
array(
'analysis_type' => sanitize_text_field( $pattern['type'] ?? 'question' ),
'topic' => sanitize_text_field( $pattern['topic'] ?? '' ),
'summary' => sanitize_textarea_field( $pattern['summary'] ?? '' ),
'frequency' => absint( $pattern['frequency'] ?? 1 ),
'relevance_score' => (float) ( $pattern['relevance_score'] ?? 0.0 ),
'source_ids' => $source_ids,
'analyzed_at' => current_time( 'mysql' ),
'metadata' => array(
'suggested_title' => $pattern['suggested_title'] ?? '',
'key_points' => $pattern['key_points'] ?? array(),
'target_keywords' => $pattern['target_keywords'] ?? array(),
),
)
);
}
return $results;

View File

@@ -49,7 +49,7 @@ class PRAutoBlogger_Content_Generator {
absint( get_option( 'prautoblogger_min_word_count', 800 ) ),
absint( get_option( 'prautoblogger_max_word_count', 2000 ) ),
get_option( 'prautoblogger_niche_description', '' ),
json_decode( get_option( 'prautoblogger_topic_exclusions', '[]' ), true ) ?: [],
json_decode( get_option( 'prautoblogger_topic_exclusions', '[]' ), true ) ?: array(),
get_option( 'prautoblogger_writing_instructions', '' )
);
@@ -70,17 +70,30 @@ class PRAutoBlogger_Content_Generator {
$model = get_option( 'prautoblogger_writing_model', PRAUTOBLOGGER_DEFAULT_WRITING_MODEL );
$response = $this->llm->send_chat_completion(
[
[ 'role' => 'system', 'content' => PRAutoBlogger_Content_Prompts::build_system( $request ) ],
[ 'role' => 'user', 'content' => PRAutoBlogger_Content_Prompts::build_single_pass( $request ) ],
],
array(
array(
'role' => 'system',
'content' => PRAutoBlogger_Content_Prompts::build_system( $request ),
),
array(
'role' => 'user',
'content' => PRAutoBlogger_Content_Prompts::build_single_pass( $request ),
),
),
$model,
[ 'temperature' => 0.7, 'max_tokens' => 4000 ]
array(
'temperature' => 0.7,
'max_tokens' => 4000,
)
);
$this->cost_tracker->log_api_call(
null, 'draft', $this->llm->get_provider_name(),
$response['model'], $response['prompt_tokens'], $response['completion_tokens']
null,
'draft',
$this->llm->get_provider_name(),
$response['model'],
$response['prompt_tokens'],
$response['completion_tokens']
);
return $response['content'];
@@ -102,17 +115,30 @@ class PRAutoBlogger_Content_Generator {
$system = PRAutoBlogger_Content_Prompts::build_system( $request );
$response = $this->llm->send_chat_completion(
[
[ 'role' => 'system', 'content' => $system ],
[ 'role' => 'user', 'content' => PRAutoBlogger_Content_Prompts::build_outline( $request ) ],
],
array(
array(
'role' => 'system',
'content' => $system,
),
array(
'role' => 'user',
'content' => PRAutoBlogger_Content_Prompts::build_outline( $request ),
),
),
$model,
[ 'temperature' => 0.5, 'max_tokens' => 1500 ]
array(
'temperature' => 0.5,
'max_tokens' => 1500,
)
);
$this->cost_tracker->log_api_call(
null, 'outline', $this->llm->get_provider_name(),
$response['model'], $response['prompt_tokens'], $response['completion_tokens']
null,
'outline',
$this->llm->get_provider_name(),
$response['model'],
$response['prompt_tokens'],
$response['completion_tokens']
);
return $response['content'];
@@ -123,17 +149,30 @@ class PRAutoBlogger_Content_Generator {
$system = PRAutoBlogger_Content_Prompts::build_system( $request );
$response = $this->llm->send_chat_completion(
[
[ 'role' => 'system', 'content' => $system ],
[ 'role' => 'user', 'content' => PRAutoBlogger_Content_Prompts::build_draft( $request, $outline ) ],
],
array(
array(
'role' => 'system',
'content' => $system,
),
array(
'role' => 'user',
'content' => PRAutoBlogger_Content_Prompts::build_draft( $request, $outline ),
),
),
$model,
[ 'temperature' => 0.7, 'max_tokens' => 4000 ]
array(
'temperature' => 0.7,
'max_tokens' => 4000,
)
);
$this->cost_tracker->log_api_call(
null, 'draft', $this->llm->get_provider_name(),
$response['model'], $response['prompt_tokens'], $response['completion_tokens']
null,
'draft',
$this->llm->get_provider_name(),
$response['model'],
$response['prompt_tokens'],
$response['completion_tokens']
);
return $response['content'];
@@ -144,17 +183,30 @@ class PRAutoBlogger_Content_Generator {
$system = PRAutoBlogger_Content_Prompts::build_system( $request );
$response = $this->llm->send_chat_completion(
[
[ 'role' => 'system', 'content' => $system ],
[ 'role' => 'user', 'content' => PRAutoBlogger_Content_Prompts::build_polish( $draft ) ],
],
array(
array(
'role' => 'system',
'content' => $system,
),
array(
'role' => 'user',
'content' => PRAutoBlogger_Content_Prompts::build_polish( $draft ),
),
),
$model,
[ 'temperature' => 0.4, 'max_tokens' => 4000 ]
array(
'temperature' => 0.4,
'max_tokens' => 4000,
)
);
$this->cost_tracker->log_api_call(
null, 'polish', $this->llm->get_provider_name(),
$response['model'], $response['prompt_tokens'], $response['completion_tokens']
null,
'polish',
$this->llm->get_provider_name(),
$response['model'],
$response['prompt_tokens'],
$response['completion_tokens']
);
return $response['content'];

View File

@@ -68,7 +68,7 @@ class PRAutoBlogger_Content_Prompts {
"- Engaging intro, strong conclusion with CTA\n" .
"- Do NOT include the title or <html>/<body> tags\n" .
"- Output HTML only, no markdown or commentary\n" .
"- Follow EVERY formatting and structural requirement from your system prompt style guide",
'- Follow EVERY formatting and structural requirement from your system prompt style guide',
$idea->get_suggested_title(),
$idea->get_topic(),
$idea->get_article_type(),
@@ -92,10 +92,10 @@ class PRAutoBlogger_Content_Prompts {
"Create a detailed outline for a blog post titled: \"%s\"\n\n" .
"Topic: %s\nArticle type: %s\n\nKey points to cover:\n%s\n\n" .
"Target keywords: %s\n\n" .
"The outline should have 4-6 main sections with bullet points under each. " .
"Include an introduction hook and a conclusion with a call to action. " .
'The outline should have 4-6 main sections with bullet points under each. ' .
'Include an introduction hook and a conclusion with a call to action. ' .
"Word count target: %d-%d words.\n\n" .
"Plan the structure to satisfy EVERY requirement in your system prompt style guide.",
'Plan the structure to satisfy EVERY requirement in your system prompt style guide.',
$idea->get_suggested_title(),
$idea->get_topic(),
$idea->get_article_type(),
@@ -125,7 +125,7 @@ class PRAutoBlogger_Content_Prompts {
"- Naturally incorporate these keywords: %s\n" .
"- Do NOT include the title in the HTML (it will be set separately)\n" .
"- Do NOT wrap in <html>, <head>, or <body> tags — just the article content\n" .
"- Follow EVERY formatting and structural requirement from your system prompt style guide",
'- Follow EVERY formatting and structural requirement from your system prompt style guide',
$outline,
$request->get_tone(),
$request->get_min_word_count(),
@@ -147,9 +147,9 @@ class PRAutoBlogger_Content_Prompts {
"3. Engagement (hooks, transitions, call-to-action)\n" .
"4. Accuracy and clarity\n" .
"5. Remove any filler or redundant sentences\n\n" .
"IMPORTANT: Preserve all bullet points, numbered lists, hyperlinks, and " .
"structural elements from the draft. Do NOT flatten lists into prose or " .
"remove links. Ensure every requirement from your system prompt style " .
'IMPORTANT: Preserve all bullet points, numbered lists, hyperlinks, and ' .
'structural elements from the draft. Do NOT flatten lists into prose or ' .
'remove links. Ensure every requirement from your system prompt style ' .
"guide is satisfied in the final output.\n\n" .
"Return the polished HTML content only. Do not add commentary.\n\n" .
"DRAFT:\n" . $draft;
@@ -172,7 +172,7 @@ class PRAutoBlogger_Content_Prompts {
$rules .= self::build_article_links();
$rules .= "--- END LINKING RULES ---";
$rules .= '--- END LINKING RULES ---';
return $rules;
}
@@ -183,20 +183,22 @@ class PRAutoBlogger_Content_Prompts {
* @return string Formatted list, or a fallback note.
*/
private static function build_article_links(): string {
$posts = get_posts( [
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => 30,
'orderby' => 'date',
'order' => 'DESC',
'fields' => 'ids',
] );
$posts = get_posts(
array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => 30,
'orderby' => 'date',
'order' => 'DESC',
'fields' => 'ids',
)
);
if ( empty( $posts ) ) {
return "No published articles available for internal linking yet.\n";
}
$lines = [ "Available article links (use where topically relevant):\n" ];
$lines = array( "Available article links (use where topically relevant):\n" );
foreach ( $posts as $post_id ) {
$title = get_the_title( $post_id );
$url = get_permalink( $post_id );
@@ -207,5 +209,4 @@ class PRAutoBlogger_Content_Prompts {
return implode( "\n", $lines ) . "\n";
}
}

View File

@@ -54,7 +54,7 @@ class PRAutoBlogger_Cost_Reporter {
public function get_daily_spend( int $days = 30 ): array {
global $wpdb;
if ( null === $wpdb ) {
return [];
return array();
}
$table = $wpdb->prefix . 'prautoblogger_generation_log';
@@ -74,8 +74,8 @@ class PRAutoBlogger_Cost_Reporter {
ARRAY_A
);
$daily = [];
foreach ( ( $results ?: [] ) as $row ) {
$daily = array();
foreach ( ( $results ?: array() ) as $row ) {
$daily[ $row['day'] ] = (float) $row['total_cost'];
}
@@ -93,7 +93,7 @@ class PRAutoBlogger_Cost_Reporter {
public function get_spend_by_stage( string $start_date, string $end_date ): array {
global $wpdb;
if ( null === $wpdb ) {
return [];
return array();
}
$table = $wpdb->prefix . 'prautoblogger_generation_log';
@@ -114,13 +114,13 @@ class PRAutoBlogger_Cost_Reporter {
ARRAY_A
);
$breakdown = [];
foreach ( ( $results ?: [] ) as $row ) {
$breakdown[ $row['stage'] ] = [
$breakdown = array();
foreach ( ( $results ?: array() ) as $row ) {
$breakdown[ $row['stage'] ] = array(
'cost' => (float) $row['total_cost'],
'tokens' => (int) $row['total_tokens'],
'calls' => (int) $row['call_count'],
];
);
}
return $breakdown;

View File

@@ -87,19 +87,21 @@ class PRAutoBlogger_Cost_Tracker {
$this->current_run_cost += $cost;
$log_entry = new PRAutoBlogger_Generation_Log( [
'post_id' => $post_id,
'run_id' => $this->run_id,
'stage' => $stage,
'provider' => $provider,
'model' => $model,
'prompt_tokens' => $prompt_tokens,
'completion_tokens' => $completion_tokens,
'estimated_cost' => $cost,
'response_status' => $response_status,
'error_message' => $error_message,
'created_at' => current_time( 'mysql' ),
] );
$log_entry = new PRAutoBlogger_Generation_Log(
array(
'post_id' => $post_id,
'run_id' => $this->run_id,
'stage' => $stage,
'provider' => $provider,
'model' => $model,
'prompt_tokens' => $prompt_tokens,
'completion_tokens' => $completion_tokens,
'estimated_cost' => $cost,
'response_status' => $response_status,
'error_message' => $error_message,
'created_at' => current_time( 'mysql' ),
)
);
global $wpdb;
$table = $wpdb->prefix . 'prautoblogger_generation_log';
@@ -158,19 +160,22 @@ class PRAutoBlogger_Cost_Tracker {
$table = $wpdb->prefix . 'prautoblogger_generation_log';
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert( $table, [
'post_id' => $post_id,
'run_id' => $this->run_id,
'stage' => $stage,
'provider' => 'cloudflare-workers-ai',
'model' => $model,
'prompt_tokens' => 0,
'completion_tokens' => 0,
'estimated_cost' => $cost_usd,
'response_status' => 'success',
'error_message' => '',
'created_at' => current_time( 'mysql' ),
] );
$wpdb->insert(
$table,
array(
'post_id' => $post_id,
'run_id' => $this->run_id,
'stage' => $stage,
'provider' => 'cloudflare-workers-ai',
'model' => $model,
'prompt_tokens' => 0,
'completion_tokens' => 0,
'estimated_cost' => $cost_usd,
'response_status' => 'success',
'error_message' => '',
'created_at' => current_time( 'mysql' ),
)
);
}
/**

View File

@@ -29,32 +29,32 @@ class PRAutoBlogger_GA4_Client {
public function fetch_data( array $posts ): array {
$property_id = get_option( 'prautoblogger_ga4_property_id', '' );
if ( '' === $property_id ) {
return [];
return array();
}
$credentials_encrypted = get_option( 'prautoblogger_ga4_credentials_json', '' );
if ( '' === $credentials_encrypted ) {
return [];
return array();
}
$credentials = PRAutoBlogger_Encryption::decrypt( $credentials_encrypted );
if ( '' === $credentials ) {
return [];
return array();
}
// Build page paths for the GA4 query.
$page_paths = [];
$path_to_id = [];
$page_paths = array();
$path_to_id = array();
foreach ( $posts as $post ) {
$path = wp_parse_url( get_permalink( $post->ID ), PHP_URL_PATH );
if ( $path ) {
$page_paths[] = $path;
$page_paths[] = $path;
$path_to_id[ $path ] = (int) $post->ID;
}
}
if ( empty( $page_paths ) ) {
return [];
return array();
}
// GA4 Data API request.
@@ -62,40 +62,45 @@ class PRAutoBlogger_GA4_Client {
$access_token = $this->get_access_token( $credentials );
if ( '' === $access_token ) {
PRAutoBlogger_Logger::instance()->error( 'GA4: Failed to get access token. Possible causes: invalid service account JSON, expired or revoked private key, or network error reaching oauth2.googleapis.com. Verify credentials in PRAutoBlogger → Settings.', 'ga4' );
return [];
return array();
}
$request_body = [
'dateRanges' => [ [ 'startDate' => '30daysAgo', 'endDate' => 'today' ] ],
'dimensions' => [ [ 'name' => 'pagePath' ] ],
'metrics' => [
[ 'name' => 'screenPageViews' ],
[ 'name' => 'averageSessionDuration' ],
[ 'name' => 'bounceRate' ],
],
'dimensionFilter' => [
'filter' => [
$request_body = array(
'dateRanges' => array(
array(
'startDate' => '30daysAgo',
'endDate' => 'today',
),
),
'dimensions' => array( array( 'name' => 'pagePath' ) ),
'metrics' => array(
array( 'name' => 'screenPageViews' ),
array( 'name' => 'averageSessionDuration' ),
array( 'name' => 'bounceRate' ),
),
'dimensionFilter' => array(
'filter' => array(
'fieldName' => 'pagePath',
'inListFilter' => [ 'values' => $page_paths ],
],
],
];
'inListFilter' => array( 'values' => $page_paths ),
),
),
);
$response = wp_remote_post(
"https://analyticsdata.googleapis.com/v1beta/properties/{$property_id}:runReport",
[
array(
'timeout' => 30,
'headers' => [
'headers' => array(
'Authorization' => 'Bearer ' . $access_token,
'Content-Type' => 'application/json',
],
),
'body' => wp_json_encode( $request_body ),
]
)
);
if ( is_wp_error( $response ) ) {
PRAutoBlogger_Logger::instance()->error( 'GA4 API request failed (WP HTTP error): ' . $response->get_error_message(), 'ga4' );
return [];
return array();
}
$ga4_status = wp_remote_retrieve_response_code( $response );
@@ -105,20 +110,20 @@ class PRAutoBlogger_GA4_Client {
sprintf( 'GA4 API request failed (HTTP %d): %s', $ga4_status, substr( $ga4_body, 0, 500 ) ),
'ga4'
);
return [];
return array();
}
$data = json_decode( wp_remote_retrieve_body( $response ), true );
$results = [];
$results = array();
foreach ( ( $data['rows'] ?? [] ) as $row ) {
foreach ( ( $data['rows'] ?? array() ) as $row ) {
$path = $row['dimensionValues'][0]['value'] ?? '';
if ( isset( $path_to_id[ $path ] ) ) {
$results[ $path_to_id[ $path ] ] = [
$results[ $path_to_id[ $path ] ] = array(
'pageviews' => (int) ( $row['metricValues'][0]['value'] ?? 0 ),
'avg_time_on_page' => (float) ( $row['metricValues'][1]['value'] ?? 0 ),
'bounce_rate' => (float) ( $row['metricValues'][2]['value'] ?? 0 ),
];
);
}
}
@@ -140,19 +145,26 @@ class PRAutoBlogger_GA4_Client {
// Build JWT for service account auth.
$now = time();
$header = wp_json_encode( [ 'alg' => 'RS256', 'typ' => 'JWT' ] );
$claim = wp_json_encode( [
'iss' => $creds['client_email'],
'scope' => 'https://www.googleapis.com/auth/analytics.readonly',
'aud' => 'https://oauth2.googleapis.com/token',
'exp' => $now + 3600,
'iat' => $now,
] );
$header = wp_json_encode(
array(
'alg' => 'RS256',
'typ' => 'JWT',
)
);
$claim = wp_json_encode(
array(
'iss' => $creds['client_email'],
'scope' => 'https://www.googleapis.com/auth/analytics.readonly',
'aud' => 'https://oauth2.googleapis.com/token',
'exp' => $now + 3600,
'iat' => $now,
)
);
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$base64_header = rtrim( strtr( base64_encode( $header ), '+/', '-_' ), '=' );
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$base64_claim = rtrim( strtr( base64_encode( $claim ), '+/', '-_' ), '=' );
$base64_claim = rtrim( strtr( base64_encode( $claim ), '+/', '-_' ), '=' );
$signing_input = $base64_header . '.' . $base64_claim;
$signature = '';
@@ -171,13 +183,13 @@ class PRAutoBlogger_GA4_Client {
$response = wp_remote_post(
'https://oauth2.googleapis.com/token',
[
array(
'timeout' => 15,
'body' => [
'body' => array(
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion' => $jwt,
],
]
),
)
);
if ( is_wp_error( $response ) ) {

View File

@@ -44,8 +44,8 @@ class PRAutoBlogger_Idea_Scorer {
* @return PRAutoBlogger_Article_Idea[] Top-ranked ideas, sorted by score desc.
*/
public function score_and_rank( array $analysis_results, int $target_count ): array {
$exclusions = json_decode( get_option( 'prautoblogger_topic_exclusions', '[]' ), true ) ?: [];
$ideas = [];
$exclusions = json_decode( get_option( 'prautoblogger_topic_exclusions', '[]' ), true ) ?: array();
$ideas = array();
$excluded_count = 0;
$deduped_count = 0;
@@ -55,11 +55,11 @@ class PRAutoBlogger_Idea_Scorer {
}
// Keyword sets kept for fallback (passed through to Semantic_Dedup).
$accepted_keyword_sets = [];
$accepted_keyword_sets = array();
foreach ( $analysis_results as $result ) {
if ( $this->is_excluded( $result->get_topic(), $exclusions ) ) {
$excluded_count++;
++$excluded_count;
continue;
}
@@ -68,7 +68,7 @@ class PRAutoBlogger_Idea_Scorer {
$topic_keywords = $this->extract_meaningful_keywords( $topic );
if ( $dedup->is_batch_duplicate( $topic, $topic_keywords, $accepted_keyword_sets ) ) {
$deduped_count++;
++$deduped_count;
PRAutoBlogger_Logger::instance()->info(
sprintf( 'Dedup(batch): skipping "%s"', substr( $topic, 0, 80 ) ),
'scorer'
@@ -77,7 +77,7 @@ class PRAutoBlogger_Idea_Scorer {
}
if ( $dedup->is_recent_duplicate( $topic, $topic_keywords ) ) {
$deduped_count++;
++$deduped_count;
PRAutoBlogger_Logger::instance()->info(
sprintf( 'Dedup(recent): skipping "%s"', substr( $topic, 0, 80 ) ),
'scorer'
@@ -89,19 +89,21 @@ class PRAutoBlogger_Idea_Scorer {
$dedup->record_accepted( $topic );
}
$metadata = $result->get_metadata() ?? [];
$metadata = $result->get_metadata() ?? array();
$ideas[] = new PRAutoBlogger_Article_Idea( [
'topic' => $result->get_topic(),
'article_type' => $this->map_type( $result->get_analysis_type() ),
'suggested_title' => $metadata['suggested_title'] ?? $result->get_topic(),
'summary' => $result->get_summary() ?? '',
'score' => $this->compute_score( $result ),
'analysis_id' => $result->get_id(),
'source_ids' => $result->get_source_ids(),
'key_points' => $metadata['key_points'] ?? [],
'target_keywords' => $metadata['target_keywords'] ?? [],
] );
$ideas[] = new PRAutoBlogger_Article_Idea(
array(
'topic' => $result->get_topic(),
'article_type' => $this->map_type( $result->get_analysis_type() ),
'suggested_title' => $metadata['suggested_title'] ?? $result->get_topic(),
'summary' => $result->get_summary() ?? '',
'score' => $this->compute_score( $result ),
'analysis_id' => $result->get_id(),
'source_ids' => $result->get_source_ids(),
'key_points' => $metadata['key_points'] ?? array(),
'target_keywords' => $metadata['target_keywords'] ?? array(),
)
);
}
PRAutoBlogger_Logger::instance()->info(
@@ -116,9 +118,12 @@ class PRAutoBlogger_Idea_Scorer {
'scorer'
);
usort( $ideas, static function ( PRAutoBlogger_Article_Idea $a, PRAutoBlogger_Article_Idea $b ): int {
return $b->get_score() <=> $a->get_score();
} );
usort(
$ideas,
static function ( PRAutoBlogger_Article_Idea $a, PRAutoBlogger_Article_Idea $b ): int {
return $b->get_score() <=> $a->get_score();
}
);
return array_slice( $ideas, 0, $target_count );
}
@@ -165,27 +170,122 @@ class PRAutoBlogger_Idea_Scorer {
* @return string[] Unique lowercase keywords.
*/
private function extract_meaningful_keywords( string $text ): array {
static $stopwords = [
'a', 'an', 'the', 'and', 'or', 'but', 'is', 'are', 'was', 'were',
'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'from', 'as',
'it', 'its', 'this', 'that', 'your', 'you', 'how', 'what', 'when',
'why', 'where', 'which', 'who', 'do', 'does', 'did', 'not', 'no',
'can', 'will', 'should', 'could', 'would', 'may', 'might', 'have',
'has', 'had', 'be', 'been', 'being', 'about', 'into', 'through',
'during', 'before', 'after', 'above', 'below', 'between', 'same',
'up', 'down', 'out', 'off', 'over', 'under', 'again', 'then',
'here', 'there', 'all', 'each', 'every', 'both', 'few', 'more',
'most', 'other', 'some', 'such', 'than', 'too', 'very', 'just',
'also', 'only', 'own', 'so', 'if', 'while', 'because', 'until',
'vs', 'versus', 'guide', 'complete', 'ultimate', 'best', 'top',
'new', 'first', 'need', 'know', 'everything',
];
static $stopwords = array(
'a',
'an',
'the',
'and',
'or',
'but',
'is',
'are',
'was',
'were',
'in',
'on',
'at',
'to',
'for',
'of',
'with',
'by',
'from',
'as',
'it',
'its',
'this',
'that',
'your',
'you',
'how',
'what',
'when',
'why',
'where',
'which',
'who',
'do',
'does',
'did',
'not',
'no',
'can',
'will',
'should',
'could',
'would',
'may',
'might',
'have',
'has',
'had',
'be',
'been',
'being',
'about',
'into',
'through',
'during',
'before',
'after',
'above',
'below',
'between',
'same',
'up',
'down',
'out',
'off',
'over',
'under',
'again',
'then',
'here',
'there',
'all',
'each',
'every',
'both',
'few',
'more',
'most',
'other',
'some',
'such',
'than',
'too',
'very',
'just',
'also',
'only',
'own',
'so',
'if',
'while',
'because',
'until',
'vs',
'versus',
'guide',
'complete',
'ultimate',
'best',
'top',
'new',
'first',
'need',
'know',
'everything',
);
$text = strtolower( $text );
$words = preg_split( '/[^a-z0-9-]+/', $text, -1, PREG_SPLIT_NO_EMPTY );
$words = array_filter( $words, static function ( string $w ) use ( $stopwords ): bool {
return strlen( $w ) >= 2 && ! in_array( $w, $stopwords, true );
} );
$words = array_filter(
$words,
static function ( string $w ) use ( $stopwords ): bool {
return strlen( $w ) >= 2 && ! in_array( $w, $stopwords, true );
}
);
return array_values( array_unique( $words ) );
}
@@ -197,13 +297,13 @@ class PRAutoBlogger_Idea_Scorer {
* @return string Article type label.
*/
private function map_type( string $analysis_type ): string {
$map = [
$map = array(
'question' => 'guide',
'complaint' => 'solution',
'comparison' => 'comparison',
'news' => 'news',
'guide' => 'guide',
];
);
return $map[ $analysis_type ] ?? 'article';
}
}

View File

@@ -61,10 +61,10 @@ class PRAutoBlogger_Image_Media_Sideloader {
}
// Prepare file array for media_handle_sideload().
$file_array = [
$file_array = array(
'name' => $this->generate_filename( $image_data['model'], $image_data['width'], $image_data['height'] ),
'tmp_name' => $temp_file,
];
);
// Import the file into media library.
$attachment_id = media_handle_sideload( $file_array, $post_id );
@@ -108,8 +108,8 @@ class PRAutoBlogger_Image_Media_Sideloader {
* @return string|\WP_Error Path to temporary file on success, WP_Error on failure.
*/
private function create_temp_file( string $bytes, string $mime_type ) {
$temp_dir = get_temp_dir();
$filename = 'prautoblogger_img_' . wp_generate_uuid4() . $this->get_extension_for_mime( $mime_type );
$temp_dir = get_temp_dir();
$filename = 'prautoblogger_img_' . wp_generate_uuid4() . $this->get_extension_for_mime( $mime_type );
$temp_path = $temp_dir . $filename;
$written = file_put_contents( $temp_path, $bytes );
@@ -156,11 +156,11 @@ class PRAutoBlogger_Image_Media_Sideloader {
* @return string File extension with leading dot (e.g., '.png').
*/
private function get_extension_for_mime( string $mime_type ): string {
$map = [
$map = array(
'image/png' => '.png',
'image/jpeg' => '.jpg',
'image/jpg' => '.jpg',
];
);
return $map[ strtolower( $mime_type ) ] ?? '.png';
}

View File

@@ -106,15 +106,15 @@ class PRAutoBlogger_Image_NSFW_Retry {
'image_pipeline'
);
$retry_request = [
$retry_request = array(
'prompt' => $fallback['prompt'],
'width' => (int) ( $batch_requests[ $key ]['width'] ?? 0 ),
'width' => (int) ( $batch_requests[ $key ]['width'] ?? 0 ),
'height' => (int) ( $batch_requests[ $key ]['height'] ?? 0 ),
'options' => $batch_requests[ $key ]['options'] ?? [],
];
'options' => $batch_requests[ $key ]['options'] ?? array(),
);
$retry_results = $this->provider->generate_image_batch( [ $key => $retry_request ] );
$retry_entry = $retry_results[ $key ] ?? [ 'error' => 'Retry provider returned no result.' ];
$retry_results = $this->provider->generate_image_batch( array( $key => $retry_request ) );
$retry_entry = $retry_results[ $key ] ?? array( 'error' => 'Retry provider returned no result.' );
if ( isset( $retry_entry['error'] ) ) {
PRAutoBlogger_Logger::instance()->warning(

View File

@@ -47,10 +47,10 @@ class PRAutoBlogger_Image_Pipeline {
?PRAutoBlogger_Image_Provider_Interface $provider = null,
?PRAutoBlogger_Cost_Tracker $cost_tracker = null
) {
$this->provider = $provider ?? self::create_default_provider();
$this->prompt_builder = new PRAutoBlogger_Image_Prompt_Builder();
$this->sideloader = new PRAutoBlogger_Image_Media_Sideloader();
$this->cost_tracker = $cost_tracker ?? new PRAutoBlogger_Cost_Tracker();
$this->provider = $provider ?? self::create_default_provider();
$this->prompt_builder = new PRAutoBlogger_Image_Prompt_Builder();
$this->sideloader = new PRAutoBlogger_Image_Media_Sideloader();
$this->cost_tracker = $cost_tracker ?? new PRAutoBlogger_Cost_Tracker();
}
/**
@@ -85,7 +85,10 @@ class PRAutoBlogger_Image_Pipeline {
array $article_data,
?array $source_data = null
): array {
$result = [ 'cost_usd' => 0.0, 'errors' => [] ];
$result = array(
'cost_usd' => 0.0,
'errors' => array(),
);
if ( ! get_option( 'prautoblogger_image_enabled' ) ) {
PRAutoBlogger_Logger::instance()->info( 'Image generation disabled in settings.', 'image_pipeline' );
@@ -107,24 +110,24 @@ class PRAutoBlogger_Image_Pipeline {
// Build all prompts upfront (LLM calls happen here, sequentially).
$article_prompt = $this->prompt_builder->build_article_prompt( $article_data );
$batch_requests = [
'image_a' => [
$batch_requests = array(
'image_a' => array(
'prompt' => $article_prompt['prompt'],
'width' => self::DEFAULT_WIDTH,
'height' => self::DEFAULT_HEIGHT,
],
];
$captions = [ 'image_a' => $article_prompt['caption'] ];
),
);
$captions = array( 'image_a' => $article_prompt['caption'] );
$source_prompt = null;
if ( $generate_b ) {
$source_prompt = $this->prompt_builder->build_source_prompt( $source_data );
$batch_requests['image_b'] = [
$source_prompt = $this->prompt_builder->build_source_prompt( $source_data );
$batch_requests['image_b'] = array(
'prompt' => $source_prompt['prompt'],
'width' => self::DEFAULT_WIDTH,
'height' => self::DEFAULT_HEIGHT,
];
$captions['image_b'] = $source_prompt['caption'];
);
$captions['image_b'] = $source_prompt['caption'];
}
// Fire all image generation requests in parallel.
@@ -290,9 +293,11 @@ class PRAutoBlogger_Image_Pipeline {
esc_html( $caption )
);
wp_update_post( [
'ID' => $post_id,
'post_content' => $caption_html . "\n\n" . $post->post_content,
] );
wp_update_post(
array(
'ID' => $post_id,
'post_content' => $caption_html . "\n\n" . $post->post_content,
)
);
}
}

View File

@@ -82,10 +82,10 @@ PROMPT;
$style_suffix = get_option( 'prautoblogger_image_style_suffix', PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX );
return [
return array(
'prompt' => trim( $parsed['scene'] . ' ' . $style_suffix ),
'caption' => $parsed['caption'],
];
);
}
/**
@@ -97,17 +97,17 @@ PROMPT;
*/
public function build_source_prompt( array $source_data ): array {
$title = $source_data['title'] ?? 'Reddit Discussion';
$comments = $source_data['comments'] ?? [];
$comments = $source_data['comments'] ?? array();
$context = is_array( $comments ) && ! empty( $comments ) ? $comments[0] : '';
$parsed = $this->rewrite_via_llm( $title, $context );
$style_suffix = get_option( 'prautoblogger_image_style_suffix', PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX );
return [
return array(
'prompt' => trim( $parsed['scene'] . ' ' . $style_suffix ),
'caption' => $parsed['caption'],
];
);
}
/**
@@ -146,15 +146,21 @@ PROMPT;
$system = $this->resolve_system_prompt();
$result = $llm->send_chat_completion(
[
[ 'role' => 'system', 'content' => $system ],
[ 'role' => 'user', 'content' => $user_message ],
],
array(
array(
'role' => 'system',
'content' => $system,
),
array(
'role' => 'user',
'content' => $user_message,
),
),
$model,
[
array(
'temperature' => 0.7,
'max_tokens' => self::REWRITER_MAX_TOKENS,
]
)
);
$raw = trim( $result['content'] ?? '' );
@@ -217,11 +223,17 @@ PROMPT;
$caption = trim( $parts[1] );
// Strip surrounding quotes from caption — the LLM wraps it in quotes.
$caption = trim( $caption, '"\'' );
return [ 'scene' => $scene, 'caption' => $caption ];
return array(
'scene' => $scene,
'caption' => $caption,
);
}
// No blank-line separator found — treat entire response as scene.
return [ 'scene' => trim( $raw ), 'caption' => '' ];
return array(
'scene' => trim( $raw ),
'caption' => '',
);
}
/**
@@ -256,10 +268,10 @@ PROMPT;
public function build_fallback_prompt( string $title ): array {
$parsed = $this->synthesize_visual_concepts_fallback( $title, '' );
$style_suffix = get_option( 'prautoblogger_image_style_suffix', PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX );
return [
return array(
'prompt' => trim( $parsed['scene'] . ' ' . $style_suffix ),
'caption' => $parsed['caption'],
];
);
}
/**
@@ -278,10 +290,10 @@ PROMPT;
$title
);
return [
return array(
'scene' => trim( $scene ),
'caption' => 'Science is full of surprises.',
];
);
}
/** Lazy-load the OpenRouter provider. */

View File

@@ -26,19 +26,19 @@ class PRAutoBlogger_Logger {
public const LEVEL_INFO = 2;
public const LEVEL_DEBUG = 3;
private const LEVEL_MAP = [
private const LEVEL_MAP = array(
'error' => self::LEVEL_ERROR,
'warning' => self::LEVEL_WARNING,
'info' => self::LEVEL_INFO,
'debug' => self::LEVEL_DEBUG,
];
);
private const LEVEL_LABELS = [
private const LEVEL_LABELS = array(
self::LEVEL_ERROR => 'error',
self::LEVEL_WARNING => 'warning',
self::LEVEL_INFO => 'info',
self::LEVEL_DEBUG => 'debug',
];
);
/** Singleton instance. */
private static ?self $instance = null;
@@ -66,7 +66,7 @@ class PRAutoBlogger_Logger {
* @param string $context Origin class or pipeline stage.
* @param array<string, mixed> $meta Optional structured data.
*/
public function error( string $message, string $context = '', array $meta = [] ): void {
public function error( string $message, string $context = '', array $meta = array() ): void {
$this->log( self::LEVEL_ERROR, $message, $context, $meta );
}
@@ -77,7 +77,7 @@ class PRAutoBlogger_Logger {
* @param string $context
* @param array<string, mixed> $meta
*/
public function warning( string $message, string $context = '', array $meta = [] ): void {
public function warning( string $message, string $context = '', array $meta = array() ): void {
$this->log( self::LEVEL_WARNING, $message, $context, $meta );
}
@@ -88,7 +88,7 @@ class PRAutoBlogger_Logger {
* @param string $context
* @param array<string, mixed> $meta
*/
public function info( string $message, string $context = '', array $meta = [] ): void {
public function info( string $message, string $context = '', array $meta = array() ): void {
$this->log( self::LEVEL_INFO, $message, $context, $meta );
}
@@ -99,7 +99,7 @@ class PRAutoBlogger_Logger {
* @param string $context
* @param array<string, mixed> $meta
*/
public function debug( string $message, string $context = '', array $meta = [] ): void {
public function debug( string $message, string $context = '', array $meta = array() ): void {
$this->log( self::LEVEL_DEBUG, $message, $context, $meta );
}
@@ -134,13 +134,16 @@ class PRAutoBlogger_Logger {
$table = $wpdb->prefix . 'prautoblogger_event_log';
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert( $table, [
'level' => $label,
'context' => substr( $context, 0, 100 ),
'message' => substr( $message, 0, 2000 ),
'meta_json' => ! empty( $meta ) ? wp_json_encode( $meta ) : null,
'created_at' => current_time( 'mysql' ),
] );
$wpdb->insert(
$table,
array(
'level' => $label,
'context' => substr( $context, 0, 100 ),
'message' => substr( $message, 0, 2000 ),
'meta_json' => ! empty( $meta ) ? wp_json_encode( $meta ) : null,
'created_at' => current_time( 'mysql' ),
)
);
}
/**
@@ -161,8 +164,8 @@ class PRAutoBlogger_Logger {
): array {
global $wpdb;
$table = $wpdb->prefix . 'prautoblogger_event_log';
$where = [];
$params = [];
$where = array();
$params = array();
if ( 'all' !== $level_filter && isset( self::LEVEL_MAP[ $level_filter ] ) ) {
$where[] = 'level = %s';
@@ -201,7 +204,10 @@ class PRAutoBlogger_Logger {
ARRAY_A
);
return [ 'rows' => $rows ?: [], 'total' => $total ];
return array(
'rows' => $rows ?: array(),
'total' => $total,
);
}
/**

View File

@@ -35,8 +35,8 @@ class PRAutoBlogger_Metrics_Collector {
}
$ga4_client = new PRAutoBlogger_GA4_Client();
$ga4_data = $ga4_client->fetch_data( $posts );
$scored = 0;
$ga4_data = $ga4_client->fetch_data( $posts );
$scored = 0;
foreach ( $posts as $post ) {
$post_id = (int) $post->ID;
@@ -47,11 +47,11 @@ class PRAutoBlogger_Metrics_Collector {
continue;
}
$ga_metrics = $ga4_data[ $post_id ] ?? [];
$ga_metrics = $ga4_data[ $post_id ] ?? array();
$score = $this->compute_composite_score( $wp_metrics, $ga_metrics, $post );
$this->store_score( $post_id, $wp_metrics, $ga_metrics, $score );
$scored++;
++$scored;
}
PRAutoBlogger_Logger::instance()->info( sprintf( 'Metrics collected for %d posts.', $scored ), 'metrics' );
@@ -64,15 +64,17 @@ class PRAutoBlogger_Metrics_Collector {
* @return \WP_Post[]
*/
private function get_generated_posts(): array {
$query = new \WP_Query( [
'post_type' => 'post',
'post_status' => 'publish',
'meta_key' => '_prautoblogger_generated',
'meta_value' => '1',
'posts_per_page' => 100,
'orderby' => 'date',
'order' => 'DESC',
] );
$query = new \WP_Query(
array(
'post_type' => 'post',
'post_status' => 'publish',
'meta_key' => '_prautoblogger_generated',
'meta_value' => '1',
'posts_per_page' => 100,
'orderby' => 'date',
'order' => 'DESC',
)
);
return $query->posts;
}
@@ -93,10 +95,10 @@ class PRAutoBlogger_Metrics_Collector {
return null;
}
return [
return array(
'comment_count' => (int) ( $post->comment_count ?? 0 ),
'days_published' => max( 1, (int) ( ( time() - strtotime( $post->post_date ) ) / DAY_IN_SECONDS ) ),
];
);
}
@@ -110,10 +112,10 @@ class PRAutoBlogger_Metrics_Collector {
* @return array{score: float, factors: array<string, float>}
*/
private function compute_composite_score( array $wp_metrics, array $ga_metrics, \WP_Post $post ): array {
$factors = [];
$factors = array();
// Comment engagement (0-25 points).
$comments_per_day = $wp_metrics['comment_count'] / max( 1, $wp_metrics['days_published'] );
$comments_per_day = $wp_metrics['comment_count'] / max( 1, $wp_metrics['days_published'] );
$factors['comments'] = min( 25.0, $comments_per_day * 25.0 );
// Pageview performance (0-30 points, if GA4 available).
@@ -142,10 +144,10 @@ class PRAutoBlogger_Metrics_Collector {
$total = array_sum( $factors );
return [
return array(
'score' => round( $total, 2 ),
'factors' => $factors,
];
);
}
/**
@@ -162,16 +164,18 @@ class PRAutoBlogger_Metrics_Collector {
global $wpdb;
$table = $wpdb->prefix . 'prautoblogger_content_scores';
$entry = new PRAutoBlogger_Content_Score( [
'post_id' => $post_id,
'pageviews' => $ga_metrics['pageviews'] ?? 0,
'avg_time_on_page' => $ga_metrics['avg_time_on_page'] ?? 0.0,
'bounce_rate' => $ga_metrics['bounce_rate'] ?? 0.0,
'comment_count' => $wp_metrics['comment_count'] ?? 0,
'composite_score' => $score_data['score'],
'score_factors' => $score_data['factors'],
'measured_at' => current_time( 'mysql' ),
] );
$entry = new PRAutoBlogger_Content_Score(
array(
'post_id' => $post_id,
'pageviews' => $ga_metrics['pageviews'] ?? 0,
'avg_time_on_page' => $ga_metrics['avg_time_on_page'] ?? 0.0,
'bounce_rate' => $ga_metrics['bounce_rate'] ?? 0.0,
'comment_count' => $wp_metrics['comment_count'] ?? 0,
'composite_score' => $score_data['score'],
'score_factors' => $score_data['factors'],
'measured_at' => current_time( 'mysql' ),
)
);
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert( $table, $entry->to_db_row() );

View File

@@ -38,13 +38,19 @@ class PRAutoBlogger_Peptide_Linker {
// Build a single regex alternation of all peptide names, longest first
// so "BPC-157" matches before a hypothetical "BPC" would.
$names = array_keys( $link_map );
usort( $names, static function ( string $a, string $b ): int {
return strlen( $b ) - strlen( $a );
} );
usort(
$names,
static function ( string $a, string $b ): int {
return strlen( $b ) - strlen( $a );
}
);
$escaped = array_map( static function ( string $name ): string {
return preg_quote( $name, '/' );
}, $names );
$escaped = array_map(
static function ( string $name ): string {
return preg_quote( $name, '/' );
},
$names
);
// Word-boundary match, case-insensitive.
$pattern = '/\b(' . implode( '|', $escaped ) . ')\b/iu';
@@ -63,16 +69,18 @@ class PRAutoBlogger_Peptide_Linker {
* @return array<string, string> Name → URL pairs, case-preserved keys.
*/
private static function build_link_map(): array {
$peptides = get_posts( [
'post_type' => 'pr_peptide',
'post_status' => 'publish',
'posts_per_page' => 200,
'orderby' => 'title',
'order' => 'ASC',
'fields' => 'ids',
] );
$peptides = get_posts(
array(
'post_type' => 'pr_peptide',
'post_status' => 'publish',
'posts_per_page' => 200,
'orderby' => 'title',
'order' => 'ASC',
'fields' => 'ids',
)
);
$map = [];
$map = array();
foreach ( $peptides as $peptide_id ) {
$title = get_the_title( $peptide_id );
$url = get_permalink( $peptide_id );
@@ -86,7 +94,7 @@ class PRAutoBlogger_Peptide_Linker {
// If the name contains hyphens, also match the space-separated
// variant (e.g., "BPC 157" → same URL as "BPC-157").
if ( strpos( $title, '-' ) !== false ) {
$space_variant = str_replace( '-', ' ', $title );
$space_variant = str_replace( '-', ' ', $title );
$map[ $space_variant ] = $url;
}
}
@@ -115,7 +123,7 @@ class PRAutoBlogger_Peptide_Linker {
}
// Build a lowercase → URL lookup for case-insensitive matching.
$lower_map = [];
$lower_map = array();
foreach ( $link_map as $name => $url ) {
$lower_map[ mb_strtolower( $name ) ] = $url;
}

View File

@@ -63,7 +63,12 @@ class PRAutoBlogger_Pipeline_Runner {
$ideas = $this->orchestrate( $cost_tracker );
if ( empty( $ideas ) ) {
return [ 'generated' => 0, 'published' => 0, 'rejected' => 0, 'cost' => 0.0 ];
return array(
'generated' => 0,
'published' => 0,
'rejected' => 0,
'cost' => 0.0,
);
}
// Generate article 1 in this process — always fits within time limit.
@@ -156,7 +161,7 @@ class PRAutoBlogger_Pipeline_Runner {
private function orchestrate( PRAutoBlogger_Cost_Tracker $cost_tracker ): array {
$target = absint( get_option( 'prautoblogger_daily_article_target', 1 ) );
$enabled = json_decode( get_option( 'prautoblogger_enabled_sources', '["reddit"]' ), true );
$enabled = json_decode( get_option( 'prautoblogger_enabled_sources', '["reddit"]' ), true );
$source_labels = is_array( $enabled ) ? implode( ', ', $enabled ) : 'reddit';
/* translators: %s is a comma-separated list of enabled source names, e.g. "reddit, llm_research". */
$this->broadcast_stage( sprintf( __( 'Collecting sources from %s…', 'prautoblogger' ), $source_labels ) );
@@ -197,11 +202,11 @@ class PRAutoBlogger_Pipeline_Runner {
$ideas
);
$queue = [
$queue = array(
'run_id' => $run_id,
'ideas' => $serialized,
'results' => $first,
];
);
update_option( self::QUEUE_KEY, $queue, false );
$this->update_queue_status( $queue );
@@ -221,7 +226,11 @@ class PRAutoBlogger_Pipeline_Runner {
spawn_cron();
wp_remote_post(
site_url( 'wp-cron.php?doing_wp_cron=' . sprintf( '%.22F', microtime( true ) ) ),
[ 'timeout' => 0.01, 'blocking' => false, 'sslverify' => false ]
array(
'timeout' => 0.01,
'blocking' => false,
'sslverify' => false,
)
);
}
@@ -231,23 +240,31 @@ class PRAutoBlogger_Pipeline_Runner {
$total = $done + count( $queue['ideas'] );
$current = get_transient( 'prautoblogger_generation_status' );
set_transient( 'prautoblogger_generation_status', [
'status' => 'running',
'stage' => sprintf( __( 'Generating article %1$d of %2$d…', 'prautoblogger' ), $done + 1, $total ),
'started' => is_array( $current ) ? ( $current['started'] ?? time() ) : time(),
'last_updated' => time(),
], 600 );
set_transient(
'prautoblogger_generation_status',
array(
'status' => 'running',
'stage' => sprintf( __( 'Generating article %1$d of %2$d…', 'prautoblogger' ), $done + 1, $total ),
'started' => is_array( $current ) ? ( $current['started'] ?? time() ) : time(),
'last_updated' => time(),
),
600
);
}
/** Write the final "complete" status transient. */
private function write_final_status( array $r ): void {
set_transient( 'prautoblogger_generation_status', [
'status' => 'complete',
'generated' => $r['generated'],
'published' => $r['published'],
'rejected' => $r['rejected'],
'cost' => $r['cost'],
], 600 );
set_transient(
'prautoblogger_generation_status',
array(
'status' => 'complete',
'generated' => $r['generated'],
'published' => $r['published'],
'rejected' => $r['rejected'],
'cost' => $r['cost'],
),
600
);
}
/** Clean up a stale or empty queue. */
@@ -265,7 +282,10 @@ class PRAutoBlogger_Pipeline_Runner {
PRAutoBlogger_Logger::instance()->info(
sprintf(
'Pipeline complete: %d generated, %d published, %d rejected. Cost: $%.4f',
$r['generated'], $r['published'], $r['rejected'], $r['cost']
$r['generated'],
$r['published'],
$r['rejected'],
$r['cost']
),
'pipeline'
);

View File

@@ -21,12 +21,12 @@ class PRAutoBlogger_Post_Assembler {
* @param PRAutoBlogger_Article_Idea $idea Article idea with type and keywords.
*/
public static function assign_taxonomy_terms( int $post_id, PRAutoBlogger_Article_Idea $idea ): void {
$type_category_map = [
$type_category_map = array(
'guide' => 'Guides',
'solution' => 'Solutions',
'comparison' => 'Comparisons',
'article' => 'Articles',
];
);
$category_name = $type_category_map[ $idea->get_article_type() ] ?? 'Articles';
$category = get_term_by( 'name', $category_name, 'category' );
@@ -34,10 +34,10 @@ class PRAutoBlogger_Post_Assembler {
if ( ! $category ) {
$result = wp_insert_term( $category_name, 'category' );
if ( ! is_wp_error( $result ) ) {
wp_set_post_categories( $post_id, [ $result['term_id'] ] );
wp_set_post_categories( $post_id, array( $result['term_id'] ) );
}
} else {
wp_set_post_categories( $post_id, [ $category->term_id ] );
wp_set_post_categories( $post_id, array( $category->term_id ) );
}
$keywords = $idea->get_target_keywords();
@@ -133,34 +133,37 @@ class PRAutoBlogger_Post_Assembler {
return; // No articles produced — nothing to amortize into.
}
$total_cost = (float) $research_row->estimated_cost;
$amortized_cost = $total_cost / $post_count;
$total_prompt = (int) $research_row->prompt_tokens;
$total_completion = (int) $research_row->completion_tokens;
$amortized_prompt = (int) round( $total_prompt / $post_count );
$amortized_compl = (int) round( $total_completion / $post_count );
$total_cost = (float) $research_row->estimated_cost;
$amortized_cost = $total_cost / $post_count;
$total_prompt = (int) $research_row->prompt_tokens;
$total_completion = (int) $research_row->completion_tokens;
$amortized_prompt = (int) round( $total_prompt / $post_count );
$amortized_compl = (int) round( $total_completion / $post_count );
// Insert one amortized research row per article.
foreach ( $post_ids as $post_id ) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert( $table, [
'post_id' => (int) $post_id,
'run_id' => $run_id,
'stage' => 'llm_research',
'provider' => $research_row->provider,
'model' => $research_row->model,
'prompt_tokens' => $amortized_prompt,
'completion_tokens' => $amortized_compl,
'estimated_cost' => $amortized_cost,
'response_status' => 'success',
'error_message' => '',
'created_at' => current_time( 'mysql' ),
] );
$wpdb->insert(
$table,
array(
'post_id' => (int) $post_id,
'run_id' => $run_id,
'stage' => 'llm_research',
'provider' => $research_row->provider,
'model' => $research_row->model,
'prompt_tokens' => $amortized_prompt,
'completion_tokens' => $amortized_compl,
'estimated_cost' => $amortized_cost,
'response_status' => 'success',
'error_message' => '',
'created_at' => current_time( 'mysql' ),
)
);
}
// Remove the original unlinked row — it's been replaced by per-article rows.
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->delete( $table, [ 'id' => (int) $research_row->id ] );
$wpdb->delete( $table, array( 'id' => (int) $research_row->id ) );
PRAutoBlogger_Logger::instance()->info(
sprintf(
@@ -213,7 +216,13 @@ class PRAutoBlogger_Post_Assembler {
return $author_id;
}
$admins = get_users( [ 'role' => 'administrator', 'number' => 1, 'fields' => 'ID' ] );
$admins = get_users(
array(
'role' => 'administrator',
'number' => 1,
'fields' => 'ID',
)
);
return ! empty( $admins ) ? (int) $admins[0] : 1;
}

View File

@@ -81,14 +81,14 @@ class PRAutoBlogger_Publisher {
$clean_content = PRAutoBlogger_Post_Assembler::sanitize_llm_content( $content );
$linked_content = PRAutoBlogger_Peptide_Linker::inject_links( $clean_content );
$post_data = [
$post_data = array(
'post_title' => sanitize_text_field( $idea->get_suggested_title() ),
'post_content' => wp_kses_post( $linked_content ),
'post_status' => $post_status,
'post_type' => 'post',
'post_author' => PRAutoBlogger_Post_Assembler::get_default_author_id(),
'meta_input' => $this->build_meta( $idea, $review, $run_id ),
];
);
/** @see class-prautoblogger.php — listeners registered in main loader. */
$post_data = apply_filters( 'prautoblogger_filter_post_data', $post_data, $idea, $review );
@@ -135,7 +135,7 @@ class PRAutoBlogger_Publisher {
PRAutoBlogger_Editorial_Review $review,
?string $run_id = null
): array {
$meta = [
$meta = array(
'_prautoblogger_generated' => '1',
'_prautoblogger_analysis_id' => $idea->get_analysis_id(),
'_prautoblogger_source_ids' => wp_json_encode( $idea->get_source_ids() ),
@@ -149,7 +149,7 @@ class PRAutoBlogger_Publisher {
'_prautoblogger_topic' => $idea->get_topic(),
'_prautoblogger_article_type' => $idea->get_article_type(),
'_prautoblogger_target_keywords' => wp_json_encode( $idea->get_target_keywords() ),
];
);
if ( null !== $run_id && '' !== $run_id ) {
$meta['_prautoblogger_run_id'] = $run_id;
}

View File

@@ -63,7 +63,11 @@ class PRAutoBlogger_Research_Reaper {
* @return array{reaped: int, deleted: int, skipped: int}
*/
public static function reap(): array {
$stats = [ 'reaped' => 0, 'deleted' => 0, 'skipped' => 0 ];
$stats = array(
'reaped' => 0,
'deleted' => 0,
'skipped' => 0,
);
try {
global $wpdb;
@@ -115,10 +119,10 @@ class PRAutoBlogger_Research_Reaper {
if ( '' === $run_id ) {
if ( $is_stale ) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->delete( $log_table, [ 'id' => (int) $orphan->id ] );
$stats['deleted']++;
$wpdb->delete( $log_table, array( 'id' => (int) $orphan->id ) );
++$stats['deleted'];
} else {
$stats['skipped']++;
++$stats['skipped'];
}
return;
}
@@ -130,22 +134,22 @@ class PRAutoBlogger_Research_Reaper {
sprintf( 'Reaped research cost across %d articles for run %s.', count( $post_ids ), $run_id ),
'cost-tracker'
);
$stats['reaped']++;
++$stats['reaped'];
return;
}
if ( $is_stale ) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->delete( $log_table, [ 'id' => (int) $orphan->id ] );
$wpdb->delete( $log_table, array( 'id' => (int) $orphan->id ) );
PRAutoBlogger_Logger::instance()->info(
sprintf( 'Deleted 7-day-stale orphan research row for run %s — no articles to attribute.', $run_id ),
'cost-tracker'
);
$stats['deleted']++;
++$stats['deleted'];
return;
}
$stats['skipped']++;
++$stats['skipped'];
}
/**
@@ -156,15 +160,17 @@ class PRAutoBlogger_Research_Reaper {
* @return int[] Distinct post IDs.
*/
private static function find_posts_for_run_id( string $run_id ): array {
$meta_ids = get_posts( [
'post_type' => 'any',
'post_status' => 'any',
'meta_key' => '_prautoblogger_run_id',
'meta_value' => $run_id,
'fields' => 'ids',
'posts_per_page' => -1,
'suppress_filters' => true,
] );
$meta_ids = get_posts(
array(
'post_type' => 'any',
'post_status' => 'any',
'meta_key' => '_prautoblogger_run_id',
'meta_value' => $run_id,
'fields' => 'ids',
'posts_per_page' => -1,
'suppress_filters' => true,
)
);
if ( is_array( $meta_ids ) && ! empty( $meta_ids ) ) {
return array_map( 'intval', $meta_ids );
}
@@ -178,6 +184,6 @@ class PRAutoBlogger_Research_Reaper {
$run_id
)
);
return is_array( $ids ) ? array_map( 'intval', $ids ) : [];
return is_array( $ids ) ? array_map( 'intval', $ids ) : array();
}
}

View File

@@ -24,13 +24,13 @@ class PRAutoBlogger_Semantic_Dedup {
private const SIMILARITY_THRESHOLD = 0.72;
/** @var float[][] Cached embeddings for recent post titles, keyed by index. */
private array $title_embeddings = [];
private array $title_embeddings = array();
/** @var string[] Recent post titles. */
private array $recent_titles = [];
private array $recent_titles = array();
/** @var float[][] Cached embeddings for accepted ideas, accumulated per-batch. */
private array $accepted_embeddings = [];
private array $accepted_embeddings = array();
/** @var bool Whether embeddings are available (false after API failure). */
private bool $embeddings_available = true;
@@ -158,7 +158,7 @@ class PRAutoBlogger_Semantic_Dedup {
if ( null === $this->provider ) {
$this->provider = new PRAutoBlogger_OpenRouter_Embedding_Provider();
}
$result = $this->provider->get_embeddings( [ $text ] );
$result = $this->provider->get_embeddings( array( $text ) );
return $result[0] ?? null;
} catch ( \Throwable $e ) {
// Degrade gracefully — one failed embed shouldn't kill the pipeline.
@@ -220,18 +220,20 @@ class PRAutoBlogger_Semantic_Dedup {
* @return string[]
*/
private function fetch_recent_post_titles(): array {
$query = new \WP_Query( [
'post_type' => 'post',
'post_status' => [ 'publish', 'draft', 'pending' ],
'meta_key' => '_prautoblogger_generated',
'meta_value' => '1',
'date_query' => [ [ 'after' => '30 days ago' ] ],
'posts_per_page' => 100,
'fields' => 'ids',
'no_found_rows' => true,
] );
$query = new \WP_Query(
array(
'post_type' => 'post',
'post_status' => array( 'publish', 'draft', 'pending' ),
'meta_key' => '_prautoblogger_generated',
'meta_value' => '1',
'date_query' => array( array( 'after' => '30 days ago' ) ),
'posts_per_page' => 100,
'fields' => 'ids',
'no_found_rows' => true,
)
);
$titles = [];
$titles = array();
foreach ( $query->posts as $post_id ) {
$titles[] = get_the_title( $post_id );
}
@@ -245,27 +247,122 @@ class PRAutoBlogger_Semantic_Dedup {
* @return string[] Unique lowercase keywords.
*/
private function extract_meaningful_keywords( string $text ): array {
static $stopwords = [
'a', 'an', 'the', 'and', 'or', 'but', 'is', 'are', 'was', 'were',
'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'from', 'as',
'it', 'its', 'this', 'that', 'your', 'you', 'how', 'what', 'when',
'why', 'where', 'which', 'who', 'do', 'does', 'did', 'not', 'no',
'can', 'will', 'should', 'could', 'would', 'may', 'might', 'have',
'has', 'had', 'be', 'been', 'being', 'about', 'into', 'through',
'during', 'before', 'after', 'above', 'below', 'between', 'same',
'up', 'down', 'out', 'off', 'over', 'under', 'again', 'then',
'here', 'there', 'all', 'each', 'every', 'both', 'few', 'more',
'most', 'other', 'some', 'such', 'than', 'too', 'very', 'just',
'also', 'only', 'own', 'so', 'if', 'while', 'because', 'until',
'vs', 'versus', 'guide', 'complete', 'ultimate', 'best', 'top',
'new', 'first', 'need', 'know', 'everything',
];
static $stopwords = array(
'a',
'an',
'the',
'and',
'or',
'but',
'is',
'are',
'was',
'were',
'in',
'on',
'at',
'to',
'for',
'of',
'with',
'by',
'from',
'as',
'it',
'its',
'this',
'that',
'your',
'you',
'how',
'what',
'when',
'why',
'where',
'which',
'who',
'do',
'does',
'did',
'not',
'no',
'can',
'will',
'should',
'could',
'would',
'may',
'might',
'have',
'has',
'had',
'be',
'been',
'being',
'about',
'into',
'through',
'during',
'before',
'after',
'above',
'below',
'between',
'same',
'up',
'down',
'out',
'off',
'over',
'under',
'again',
'then',
'here',
'there',
'all',
'each',
'every',
'both',
'few',
'more',
'most',
'other',
'some',
'such',
'than',
'too',
'very',
'just',
'also',
'only',
'own',
'so',
'if',
'while',
'because',
'until',
'vs',
'versus',
'guide',
'complete',
'ultimate',
'best',
'top',
'new',
'first',
'need',
'know',
'everything',
);
$text = strtolower( $text );
$words = preg_split( '/[^a-z0-9-]+/', $text, -1, PREG_SPLIT_NO_EMPTY );
$words = array_filter( $words, static function ( string $w ) use ( $stopwords ): bool {
return strlen( $w ) >= 2 && ! in_array( $w, $stopwords, true );
} );
$words = array_filter(
$words,
static function ( string $w ) use ( $stopwords ): bool {
return strlen( $w ) >= 2 && ! in_array( $w, $stopwords, true );
}
);
return array_values( array_unique( $words ) );
}

View File

@@ -51,7 +51,7 @@ class PRAutoBlogger_Source_Collector {
// Only fall back to reddit if the option is corrupt (non-JSON).
// An empty array '[]' is a valid choice — the user disabled all sources.
if ( ! is_array( $enabled ) ) {
$enabled = [ 'reddit' ];
$enabled = array( 'reddit' );
}
$total_inserted = 0;
@@ -64,9 +64,9 @@ class PRAutoBlogger_Source_Collector {
}
try {
$data = $provider->collect_data( $this->get_config_for_source( $source_type ) );
$inserted = $this->store_data( $data );
$refreshed = count( $data ) - $inserted;
$data = $provider->collect_data( $this->get_config_for_source( $source_type ) );
$inserted = $this->store_data( $data );
$refreshed = count( $data ) - $inserted;
$total_inserted += $inserted;
PRAutoBlogger_Logger::instance()->info(
@@ -117,10 +117,10 @@ class PRAutoBlogger_Source_Collector {
return null;
}
return [
return array(
'title' => $row['title'] ?? 'Reddit Discussion',
'selftext' => $row['content'] ?? '',
];
);
}
/**
@@ -175,7 +175,7 @@ class PRAutoBlogger_Source_Collector {
// ON DUPLICATE KEY UPDATE returns 2 for updates, 1 for inserts.
// Only count genuine inserts toward the "new" tally.
if ( false !== $result && 1 === (int) $result ) {
$inserted++;
++$inserted;
}
}
@@ -190,10 +190,10 @@ class PRAutoBlogger_Source_Collector {
* @return PRAutoBlogger_Source_Provider_Interface|null
*/
private function get_provider( string $source_type ): ?PRAutoBlogger_Source_Provider_Interface {
$providers = [
$providers = array(
'reddit' => PRAutoBlogger_Reddit_Provider::class,
'llm_research' => PRAutoBlogger_LLM_Research_Provider::class,
];
);
/**
* Filter the registered source providers.
@@ -230,20 +230,20 @@ class PRAutoBlogger_Source_Collector {
*/
private function get_config_for_source( string $source_type ): array {
if ( 'reddit' === $source_type ) {
return [
'subreddits' => json_decode( get_option( 'prautoblogger_target_subreddits', '[]' ), true ) ?: [],
return array(
'subreddits' => json_decode( get_option( 'prautoblogger_target_subreddits', '[]' ), true ) ?: array(),
'limit' => 25,
'time_filter' => 'day',
'include_comments' => true,
'comments_per_post' => 10,
];
);
}
if ( 'llm_research' === $source_type ) {
$config = [
$config = array(
'model' => get_option( 'prautoblogger_research_model', PRAUTOBLOGGER_DEFAULT_ANALYSIS_MODEL ),
'prompt' => get_option( 'prautoblogger_research_prompt', '' ),
];
);
// Pass the pipeline's cost tracker so research costs get tagged
// with the run_id for post-pipeline amortization across articles.
if ( null !== $this->cost_tracker ) {
@@ -252,6 +252,6 @@ class PRAutoBlogger_Source_Collector {
return $config;
}
return [];
return array();
}
}