tag with specific attributes.
$allowed_html = array(
'img' => array(
'src' => array(),
'alt' => array(),
'class' => array(),
),
);
// Construct the
tag.
$img_tag = '
';
// Return sanitized HTML.
return wp_kses( $img_tag, $allowed_html );
}
/**
* Convert OpenAI Markdown to HTML.
*
* @param string $content The OpenAI Markdown content.
* @param string $title The title of the content.
*
* @since 1.0.0
* @package AIContentWriter
* @return array The HTML content and title.
*/
function aicw_convert_openai_markdown_to_html( $content, $title = '' ) {
// Replace headers.
$content = preg_replace( '/###### (.+)/', '
$1
', $content );
$content = preg_replace( '/##### (.+)/', '$1
', $content );
$content = preg_replace( '/#### (.+)/', '$1
', $content );
$content = preg_replace( '/### (.+)/', '$1
', $content );
$content = preg_replace( '/## (.+)/', '$1
', $content );
// Replace bold text.
$content = preg_replace( '/\*\*(.+?)\*\*/', '$1', $content );
// Extract title if present.
preg_match( '/^# (.+)/m', $content, $matches );
$title = $matches[1] ?? $title;
// Remove title from content.
$content = preg_replace( '/^# .+\n*/', '', $content );
return array(
'title' => $title,
'content' => $content,
);
}
/**
* Get campaign.
*
* @param mixed $data The data.
*
* @since 1.4.0
* @return WP_Post|false The campaign object, or false if not found.
*/
function aicw_get_campaign( $data ) {
if ( is_numeric( $data ) ) {
$data = get_post( $data );
}
if ( $data instanceof WP_Post && 'aicw_campaign' === $data->post_type ) {
return $data;
}
return false;
}
/**
* Get campaigns.
*
* @param array $args The args.
* @param bool $count Whether to return a count.
*
* @since 1.4.0
* @return array|int The campaigns.
*/
function aicw_get_campaigns( $args = array(), $count = false ) {
$defaults = array(
'post_type' => 'aicw_campaign',
'posts_per_page' => - 1,
'orderby' => 'date',
'order' => 'ASC',
);
$args = wp_parse_args( $args, $defaults );
$query = new WP_Query( $args );
if ( $count ) {
return $query->found_posts;
}
return array_map( 'aicw_get_campaign', $query->posts );
}
/**
* Get campaign titles.
* This will generate the titles for the campaign using OpenAI or any other AI service.
*
* @param string $keywords The campaign keywords.
*
* @since 1.4.0
* @return array The generated titles.
*/
function aicw_generate_titles( $keywords ) {
if ( empty( $keywords ) ) {
return array();
}
$api_model = get_option( 'aicw_api_model' );
if ( 'gemini' === $api_model ) {
$api_key = get_option( 'aicw_gemini_api_key' );
if ( empty( $api_key ) ) {
return array();
}
// Define the API URL.
$model_name = 'gemini-1.5-pro';
$url = 'https://generativelanguage.googleapis.com/v1/models/' . $model_name . ':generateContent';
// The data payload to send to the Gemini API.
$data = array(
'contents' => array(
array(
'role' => 'user',
'parts' => array(
array(
'text' => 'You are experienced in writing about ' . $keywords . '. Please generate more than 20 SEO friendly titles for these keywords.',
),
),
),
),
);
// Set up the arguments for the request.
$args = array(
'body' => wp_json_encode( $data ),
'headers' => array(
'Content-Type' => 'application/json',
'x-goog-api-key' => $api_key,
),
'method' => 'POST',
'data_format' => 'body',
'timeout' => 300,
);
// Make the request.
$response = wp_remote_post( $url, $args );
// Check if the request returned an error.
if ( is_wp_error( $response ) ) {
// Handle the error appropriately.
return array();
}
// Get the response body.
$body = wp_remote_retrieve_body( $response );
// Decode the JSON response.
$result = json_decode( $body, true );
// Handle the response data.
if ( ! isset( $result['candidates'][0]['content']['parts'][0]['text'] ) ) {
return array();
}
$titles = explode( "\n", trim( $result['candidates'][0]['content']['parts'][0]['text'] ) );
} elseif ( 'chatgpt' === $api_model ) {
$api_key = get_option( 'aicw_chatgpt_api_secret_key' );
if ( empty( $api_key ) ) {
return array();
}
// Call OpenAI API to generate titles.
$api_url = 'https://api.openai.com/v1/chat/completions';
// The data payload to send to the OpenAI API.
$data = array(
'model' => 'gpt-3.5-turbo',
'messages' => array(
array(
'role' => 'system',
'content' => 'You are experienced in writing about ' . $keywords . '. Please generate some titles for these keywords.',
),
array(
'role' => 'user',
'content' => 'Generate more than 20 titles for these keywords: ' . $keywords,
),
),
'max_tokens' => 1000,
'temperature' => 0.7,
'top_p' => 1,
'frequency_penalty' => 0,
'presence_penalty' => 0,
);
// Set up the arguments for the request.
$args = array(
'headers' => array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
),
'body' => wp_json_encode( $data ),
'timeout' => 300,
);
// Make the request.
$response = wp_remote_post( $api_url, $args );
// Check if the request returned an error.
if ( is_wp_error( $response ) ) {
// Handle the error appropriately.
return array();
}
// Get the response body.
$body = wp_remote_retrieve_body( $response );
// Decode the JSON response.
$result = json_decode( $body, true );
// Check if the response contains the expected data.
if ( ! isset( $result['choices'][0]['message']['content'] ) ) {
return array();
}
$titles = explode( "\n", trim( $result['choices'][0]['message']['content'] ) );
}
return $titles;
}
/**
* Generate content for the campaign.
* This will generate the content for the campaign using OpenAI or any other AI service.
*
* @param string $title The title of the content.
* @param string $keywords The campaign keywords.
*
* @since 1.4.0
* @return string|mixed The generated content or null.
*/
function aicw_generate_content( $title, $keywords = '' ) {
if ( empty( $title ) ) {
return '';
}
$api_model = get_option( 'aicw_api_model' );
if ( 'gemini' === $api_model ) {
$language = 'English'; // TODO: Add language support.
$api_key = get_option( 'aicw_gemini_api_key' );
if ( empty( $api_key ) ) {
return null;
}
// Generating prompt for the Gemini API model.
$prompt = 'Write a well-structured article on the title: ' . $title . ', in ' . $language . ',';
if ( ! empty( $keywords ) ) {
$prompt = $prompt . ' with keywords: ' . $keywords . ',';
}
// Generate content using the Google Gemini Open AI API.
$prompt_data = array(
'text' => $prompt,
);
$content = ai_content_writer()->generate_content( $prompt_data );
if ( is_wp_error( $content ) ) {
return null;
}
// Get all the matches from the generated content string starting form "**" and end with "**" as array.
$pattern = '/\*\*(.*?)\*\*/';
preg_match_all( $pattern, $content, $matches );
// Get the title.
// $title = $matches[1][0];
// Remove the title from the content.
$content = str_replace( $matches[0][0], '', $content );
if ( is_array( $matches[0] ) && is_array( $matches[1] ) ) {
foreach ( $matches[0] as $key => $match ) {
// replaces the strings.
$content = str_replace( $match, '' . $matches[1][ $key ] . '
', $content );
}
}
// only matches exactly match string "1." and end with "
".
$pattern = '/1\.(.*?)<\/h2>/';
preg_match_all( $pattern, $content, $matches_h2_1 );
if ( is_array( $matches_h2_1[0] ) & is_array( $matches_h2_1[1] ) ) {
foreach ( $matches_h2_1[0] as $key => $match ) {
// replaces the strings.
$content = str_replace( $match, '1.' . $matches_h2_1[1][ $key ] . '
', $content );
}
}
// only matches exactly match string "* " and end with "
".
$pattern = '/\* (.*?)<\/h2>/';
preg_match_all( $pattern, $content, $matches_star_h2 );
if ( is_array( $matches_star_h2[0] ) & is_array( $matches_star_h2[1] ) ) {
foreach ( $matches_star_h2[0] as $key => $match ) {
// replaces the strings.
$content = str_replace( $match, '' . $matches_star_h2[1][ $key ] . '
', $content );
}
}
// only matches exactly match string "- " and end with "
".
$pattern = '/- (.*?)<\/h2>/';
preg_match_all( $pattern, $content, $matches_dash_h2 );
if ( is_array( $matches_dash_h2[0] ) & is_array( $matches_dash_h2[1] ) ) {
foreach ( $matches_dash_h2[0] as $key => $match ) {
$content = str_replace( $match, '' . $matches_dash_h2[1][ $key ] . '
', $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 = 1000;
$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 ) ? 1000 : 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 title across all post types.
*
* @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 ) {
// TODO: The old code is commented out. It should be removed after testing.
/**
$query = new WP_Query(
array(
'title' => $post_title,
'post_type' => 'aicw_post',
'posts_per_page' => 1,
)
);
$post = $query->post;
if ( $post instanceof WP_Post && 'aicw_post' === $post->post_type ) {
return $post->ID;
}
*/
$query = new WP_Query(
array(
'title' => $post_title,
'post_type' => 'any',
'posts_per_page' => 1,
'fields' => 'ids',
'no_found_rows' => true,
)
);
return ! empty( $query->posts ) ? $query->posts[0] : null;
}
/**
* 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 needed elements.
*/
function aicw_campaign_needs_update( $campaign_id ) {
// Check if the campaign is not found.
if ( empty( $campaign_id ) ) {
return array(
'needs_update' => false,
'error' => 'The campaign is not found.',
);
}
// Check is the campaign target is reached.
$target = absint( get_post_meta( $campaign_id, '_aicw_campaign_target', true ) );
// Get total found posts for this campaign.
$total_posts = aicw_get_campaigns(
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',
),
true
);
// Get total found posts for this campaign that already published.
$total_posts_published = aicw_get_campaigns(
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',
),
true
);
// Merge the total posts and total published posts.
if ( $total_posts_published > 0 ) {
$total_posts = $total_posts + $total_posts_published;
}
// Check if the target is reached.
if ( $total_posts >= $target ) {
return array(
'needs_update' => false,
'error' => 'The campaign target is reached.',
);
}
// Check how many posts are needed to reach the target.
$posts_needed = absint( $target - $total_posts );
return array(
'needs_update' => true,
'posts_needed' => $posts_needed,
'error' => '',
);
}
/**
* Campaigns 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 needed 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', 10 );
return absint( $target_limit );
}