', $content );
}
}
return $content;
} elseif ( 'chatgpt' === $api_model ) {
$language = 'English'; // TODO: Add language support.
$api_secret_key = get_option( 'aicw_chatgpt_api_secret_key' );
if ( empty( $api_secret_key ) ) {
return null;
}
// For ChatGPT OpenAI API model.
$system_tone = '';
if ( empty( $system_tone ) ) {
$system_tone = esc_html__( 'You are an expert SEO content writer. Generate factually accurate, engaging, and well-structured articles optimized for readability and search engines.', 'ai-content-writer' );
}
$max_tokens = 5000;
$temperature = 0.7;
// Generating prompt for ChatGPT OpenAI API model.
$prompt = sprintf(
/* translators: 1: Prompt Content, 2: System Tone, 3: Language */
esc_html__(
'Write a well-structured article on %1$s with a professional tone. %2$s Ensure the content is engaging, easy to read, and formatted with proper headings (H2, H3), bullet points, and paragraphs.
Use %3$s as the language for this article.
The article should include:
- A catchy introduction that hooks the reader
- Informative body sections with subheadings
- A compelling conclusion with key takeaways
Avoid fluff, keep sentences concise, and ensure factual accuracy. Where applicable, use industry insights, statistics, and real-world examples. If necessary, cite reliable sources to back up claims. Ensure no AI or robotic toneāthe content should feel human-written.',
'ai-content-writer'
),
'"' . $title . '"',
empty( $keywords ) ? esc_html__( 'The article should be optimized for SEO', 'ai-content-writer' ) : esc_html__( 'The article should be optimized for SEO, including relevant keywords: ', 'ai-content-writer' ) . '"' . $keywords . '".',
$language
);
// Generate content using the OpenAI ChatGPT API.
$prompt_data = array(
'secret_key' => $api_secret_key,
'prompt' => $prompt,
'system_tone' => $system_tone,
'max_tokens' => empty( $max_tokens ) ? 5000 : absint( $max_tokens ),
'temperature' => empty( $temperature ) ? 0.7 : floatval( $temperature ),
);
$content = ai_content_writer()->generate_openai_content( $prompt_data );
if ( is_wp_error( $content ) ) {
return null;
}
// Convert OpenAI Markdown to HTML.
$content = aicw_convert_openai_markdown_to_html( $content, $title );
return isset( $content['content'] ) ? $content['content'] : null;
}
return null;
}
/**
* Get the post-ID by the exact title using a direct $wpdb query.
*
* @param string $post_title The post-title.
*
* @since 1.4.0
* @return int|null The post-ID if found, otherwise null.
*/
function aicw_get_post_by_title( $post_title ) {
global $wpdb;
// Encode to match how WordPress stores titles in the DB.
$post_title = htmlspecialchars( wp_unslash( $post_title ), ENT_QUOTES, 'UTF-8' );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$query = $wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM {$wpdb->posts}
WHERE post_title = %s
AND post_type != 'revision'
AND post_status IN ('publish', 'pending', 'draft', 'future', 'private')
LIMIT 1",
$post_title
)
);
return $query ? (int) $query : null;
}
/**
* Get all post-IDs by the exact title using a direct $wpdb query.
*
* @param string $post_title The post-title.
*
* @since 1.4.0
* @return int[] Array of post-IDs if found, otherwise an empty array.
*/
function aicw_get_posts_by_title( $post_title ) {
global $wpdb;
// Encode to match how WordPress stores titles in the DB.
$post_title = htmlspecialchars( wp_unslash( $post_title ), ENT_QUOTES, 'UTF-8' );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$query = $wpdb->get_col(
$wpdb->prepare(
"SELECT ID FROM {$wpdb->posts}
WHERE post_title = %s
AND post_type != 'revision'
AND post_status IN ('publish', 'pending', 'draft', 'future', 'private')",
$post_title
)
);
return array_map( 'intval', $query );
}
/**
* Campaign needs to update.
* This function checks if the campaign needs to be updated.
*
* @param int $campaign_id The campaign ID.
*
* @since 1.4.0
* @return array The array of necessary elements.
*/
function aicw_campaign_needs_update( $campaign_id ) {
if ( empty( $campaign_id ) ) {
return array(
'needs_update' => false,
'error' => __( 'The campaign is not found.', 'ai-content-writer' ),
);
}
// Check is the campaign target reached.
$target = absint( get_post_meta( $campaign_id, '_aicw_campaign_target', true ) );
// Get total pending posts for this campaign.
$pending_posts = get_posts(
array(
'post_type' => 'aicw_post',
'posts_per_page' => -1,
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
array(
'key' => '_aicw_campaign_id',
'value' => $campaign_id,
),
),
'post_status' => 'any',
'fields' => 'ids',
)
);
// Get total found posts for this campaign that already published.
$published_posts = get_posts(
array(
'post_type' => get_post_meta( $campaign_id, '_aicw_post_type', true ) ?? 'post',
'posts_per_page' => -1,
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
array(
'key' => '_aicw_campaign_id',
'value' => $campaign_id,
),
),
'post_status' => 'any',
'fields' => 'ids',
),
);
$total_posts = count( $pending_posts );
$published_posts_count = count( $published_posts );
// Merge the total posts and total published posts.
if ( $published_posts_count > 0 ) {
$total_posts = $total_posts + $published_posts_count;
}
// Check if the target is reached.
if ( $total_posts >= $target ) {
return array(
'needs_update' => false,
'posts_needed' => 0,
'pending_posts' => $pending_posts,
'error' => __( 'The campaign target is reached.', 'ai-content-writer' ),
);
}
// Check how many posts are needed to reach the target.
$posts_needed = absint( $target - $total_posts );
// Set the posts needed to max 20. Forcefully set to 20 to avoid overloading.
if ( $posts_needed >= 20 ) {
$posts_needed = 20;
}
return array(
'needs_update' => true,
'posts_needed' => $posts_needed,
'pending_posts' => $pending_posts,
'error' => '',
);
}
/**
* Campaign temporary post needs update.
* This function checks if the campaign temporary post needs to be updated.
*
* @param \WP_Post $post The temporary post.
* @param int $campaign_id The campaign ID.
*
* @since 1.4.0
* @return array The array of necessary elements.
*/
function aicw_temp_post_needs_update( $post, $campaign_id ) {
// Check if the temporary post and campaign are empty or not found.
if ( empty( $post ) || empty( $campaign_id ) ) {
return array();
}
$needs_update = array();
// Check if the temporary post-title is empty or not found.
if ( empty( $post->post_title ) ) {
$needs_update['title'] = true;
}
// Check if the temporary post-content is empty or not found.
if ( empty( $post->post_content ) ) {
$needs_update['content'] = true;
}
// Check if the campaign needs to generate a thumbnail.
$generate_thumb = get_post_meta( $campaign_id, '_aicw_generate_thumbnail', true );
// Check if the temporary post-thumbnail is empty or not found.
if ( empty( get_post_thumbnail_id( $post->ID ) ) && $generate_thumb ) {
$needs_update['thumbnail'] = true;
}
return $needs_update;
}
/**
* Update last run time.
* This function updates the last run time for the campaigns or temporary posts.
*
* @param int $post_id The post ID.
*
* @since 1.4.0
* @return void
*/
function aicw_update_last_run_time( $post_id ) {
// Update the post and indicate that the post has been updated.
wp_update_post(
array(
'ID' => $post_id,
'post_modified' => current_time( 'mysql' ),
'post_modified_gmt' => current_time( 'mysql', 1 ),
)
);
}
/**
* Get the camping target max limit.
* This function gets the campaign target maximum limit.
*
* @since 1.4.0
* @return int The campaign target limit.
*/
function aicw_get_campaign_target_max_limit() {
$target_limit = apply_filters( 'aicw_campaign_target_max_limit', 20 );
return absint( $target_limit );
}
/**
* Get the camping frequencies.
* This function gets the campaign frequencies.
*
* @since 2.2.5
* @return array The campaign frequencies.
*/
function aicw_get_campaign_frequencies(): array {
return apply_filters(
'aicw_campaign_frequencies',
array(
'hourly' => __( 'Hourly', 'ai-content-writer' ),
'twicedaily' => __( 'Twice Daily', 'ai-content-writer' ),
'daily' => __( 'Daily', 'ai-content-writer' ),
'weekly' => __( 'Weekly', 'ai-content-writer' ),
'monthly' => __( 'Monthly', 'ai-content-writer' ),
)
);
}
/**
* Get user agents.
* This function gets the user agents for the campaigns.
*
* @since 1.7.0
* @return array The user agents.
*/
function aicw_user_agents(): array {
return array(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
'Mozilla/5.0 (iPad; CPU OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
);
}
/**
* Render search and replace fields.
*
* @param array $field Field arguments.
*
* @since 2.0.2
*/
function aicw_search_replace_fields( $field ) {
$key = wp_rand( 1, 100000 );
?>
0 ) { $u .= str_repeat( '=', 4 - $padding );
}
// Decode the Base64 string. The base64_decode function is safe here as we are validating the input.
$decoded_url = base64_decode( $u, true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
if ( false !== $decoded_url ) {
return esc_url_raw( $decoded_url );
}
}
return false;
}
/**
* Render an inline SVG icon from the curated icon set.
*
* Icons are stroke based (24x24, currentColor) so they inherit the surrounding
* text color and stay crisp at any size.
*
* @param string $name Icon name.
*
* @since 2.2.7
* @return string SVG markup, or an empty string when the icon is unknown.
*/
function aicw_get_icon( $name ) {
$icons = array(
'check' => '',
'settings' => '',
'arrow-right' => '',
'sparkles' => '',
'rocket' => '',
'help' => '',
'book' => '',
'layers' => '',
);
if ( ! isset( $icons[ $name ] ) ) {
return '';
}
return '';
}
/**
* Allowed HTML for rendering inline SVG icons with wp_kses().
*
* @since 2.2.7
* @return array Allowed HTML tags and attributes for SVG icons.
*/
function aicw_get_svg_allowed_html() {
$attrs = array(
'class' => true,
'width' => true,
'height' => true,
'viewbox' => true,
'fill' => true,
'stroke' => true,
'stroke-width' => true,
'stroke-linecap' => true,
'stroke-linejoin' => true,
'aria-hidden' => true,
'focusable' => true,
);
return array(
'svg' => $attrs,
'g' => array( 'fill' => true, 'clip-path' => true ),
'path' => array( 'd' => true, 'fill' => true ),
'circle' => array( 'cx' => true, 'cy' => true, 'r' => true ),
'line' => array( 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true ),
'polyline' => array( 'points' => true ),
'polygon' => array( 'points' => true ),
'rect' => array( 'x' => true, 'y' => true, 'width' => true, 'height' => true, 'rx' => true ),
);
}