'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' ); if ( ! $category ) { $result = wp_insert_term( $category_name, 'category' ); if ( ! is_wp_error( $result ) ) { wp_set_post_categories( $post_id, [ $result['term_id'] ] ); } } else { wp_set_post_categories( $post_id, [ $category->term_id ] ); } $keywords = $idea->get_target_keywords(); if ( ! empty( $keywords ) ) { wp_set_post_tags( $post_id, $keywords, true ); } } /** * Link generation log entries to the newly created post. * Uses run_id for precise linking; falls back to timestamp for legacy data. * * @param int $post_id Post ID. * @param string|null $run_id Pipeline run identifier, or null for legacy fallback. */ public static function link_generation_logs( int $post_id, ?string $run_id = null ): void { global $wpdb; $table = $wpdb->prefix . 'prautoblogger_generation_log'; if ( null !== $run_id && '' !== $run_id ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query( $wpdb->prepare( "UPDATE {$table} SET post_id = %d WHERE post_id IS NULL AND run_id = %s", $post_id, $run_id ) ); } else { // Legacy fallback: timestamp-based linking for pre-migration log entries. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query( $wpdb->prepare( "UPDATE {$table} SET post_id = %d WHERE post_id IS NULL AND created_at >= %s", $post_id, gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS ) ) ); } } /** * Strip common LLM output artifacts from generated content. * Removes markdown fences, HTML document wrappers, stray head-level tags. * * @param string $content Raw LLM output. * @return string Cleaned HTML suitable for post_content. */ public static function sanitize_llm_content( string $content ): string { $content = trim( $content ); // Strip markdown code fences: ```html ... ``` or ``` ... ``` if ( preg_match( '/^```(?:\w+)?\s*\n(.*?)```\s*$/s', $content, $matches ) ) { $content = trim( $matches[1] ); } // Strip full HTML document wrappers — extract only
inner content. if ( preg_match( '/]*>(.*)<\/body>/is', $content, $matches ) ) { $content = trim( $matches[1] ); } // Remove stray document-level tags. $content = preg_replace( '/<\/?(html|head|body|meta|title|!DOCTYPE)[^>]*>/i', '', $content ); $content = preg_replace( '/]*>/i', '', $content ); $content = preg_replace( '/