Join the Voxel Guide Community!

Get Involved

Voxel Theme Version: 1.5.4 | Elementor Plugin Version: 3.28.0

You are now eligible to list your profile publicly!

Voxel AI Review Summary - powered by Chat GPT

Description/Instructions

Voxel AI Review Summary plugin provides two powerful shortcodes that let you easily display a review summary on your website. These shortcodes work with the Voxel review system and use ChatGPT to generate dynamic, AI-powered content (summary) based on user reviews. The plugin also caches the results so that summaries update only when new reviews are added.

Shortcode 1: [ review_summary ]

Purpose:
This shortcode generates a concise summary of all user reviews for a post. The summary is produced by sending the combined review content to the ChatGPT API, which returns a brief overview highlighting the main strengths and weaknesses of the listing—similar to the summary you might see on TripAdvisor or other directories.

Usage:

Basic: Simply add [ review_summary ] (without the spaces) to the post template. If no post ID is specified, the current post is used.

Specific Post: Use [ review_summary post_id=”123″ ] (without the spaces) to generate a summary for a specific post (where “123” is the post ID).

How It Works:

  1. The plugin retrieves all reviews for the specified post from the Voxel timeline table.
  2. It checks if a summary is already cached along with the current review count. If the review count hasn’t changed, the cached summary is used.
  3. If there are new reviews, it combines the review texts, constructs a prompt, and sends it to the ChatGPT API.
  4. The returned summary is then cleaned (for example, any unwanted leading “Summary:” text is removed), cached, and output.
  5. The final output is wrapped in a container  (review-summary-wrapper) you can style it separately.

Shortcode 2: [ category_opinions ]

Purpose:
This shortcode generates a one‑word summary (an opinion) for each review category you specify. By default, it uses the following categories: Food, Atmosphere, Service, and Value. (You can customize this list to match your existing review categories by passing a comma‑separated list via the shortcode attributes.) Each category’s summary is generated using ChatGPT based on all reviews for the post and is cached until new reviews are added.

Usage:

Default: Simply add [ category_opinions ] (without the spaces) to your content, and it will use the default categories: “Food, Atmosphere, Service, Value.”

Custom Categories: To use a custom list, add the categories attribute, for example: [ category_opinions categories=”Cuisine, Ambience, Hospitality, Pricing” ] (without the spaces) 

Specific Post: You can also specify a post ID, e.g.,  [ category_opinions post_id=”123″ categories=”Food, Atmosphere, Service, Value” ] (without the spaces) 

How It Works:

  1. The plugin retrieves all reviews for the specified (or current) post.
  2. For each category provided (via the categories attribute), it checks if a one‑word opinion is already cached (based on the current number of reviews).
  3. If the cached value is available and the review count hasn’t changed, it uses that cached opinion.
  4. Otherwise, it combines all review texts, builds a prompt that asks ChatGPT to provide one word summarizing the overall opinion about that category (e.g., “Delicious” for Food), and sends it to the ChatGPT API.
  5. The returned one‑word opinion is then cleaned up (removing any non-letter characters), cached, and output in its own box.
  6. The final output is a grid of boxes (one for each category) displaying the category label and the one‑word opinion.

Additional Notes

Caching:
Both shortcodes cache their outputs along with the review count. This means that if no new reviews are added, the summary and opinions will remain the same even if the page is reloaded—minimizing API calls and speeding up page loads. However, there is a setting to delete all cached summaries and category opinions. The plugin will then regenerate the summaries on the next page load.

API Configuration:
Before using these shortcodes, you must configure your ChatGPT API key in the plugin settings. In your WordPress dashboard, navigate to Settings > Voxel AI Review Summary and enter your OpenAI API key.

IMPORTANT:  If the code snippet provided in the Snippet Section below is broken, please get the code here.

Customization:
You can further customize the prompts, API parameters (such as temperature), and inline styling (or use custom CSS) to match your site’s design.

The two separate shortcodes are intentional. It allows you to either display just a Summary or both the Summary and the one-word Category Opinions, side by side. Just can add a two column container and a shortcode into each column then style as needed. 

Additionally,  I recommend setting visibility rules to the main container to hide it if there are fewer than 2 or 3 reviews. Otherwise the summary will not read well… you get something like this:
“It seems that there is only one review provided, which states “I did like it.” Unfortunately, there is not enough information to provide a detailed summary of main strengths and weaknesses mentioned by users.”

Instructions

Activate the Plugin:
Create a file and name it voxel-ai-review-summary.php,  copy the code snippet and paste it into the file, and upload it to your wp-content/plugins/ folder and activate it.

Configure API Key:
In your WordPress dashboard, go to Settings > Voxel AI Review Summary and enter your ChatGPT API key.
To create a ChatGPT API Key,  click here (Costs apply)

Cache Refresh Option:

On the settings page, you'll also find a Refresh Cached Summaries button. Click this button to delete all cached summaries and category opinions. The plugin will then regenerate the summaries on the next page load.

Insert Shortcodes in Your Content:

To display a review summary: [ review_summary ] or [ review_summary post_id="123" ]

To display category opinions (with custom categories if desired):  [ category_opinions categories="Food, Atmosphere, Service, Value" ]
 

IMPORTANT:  If the code snippet provided in the Snippet Section below is broken, please get the code here.

<?php
/*
Plugin Name: Voxel AI Review Summary
Description: Connects to the ChatGPT API to generate a review summary, one-word category opinions, and a blog post summary from Voxel reviews. All outputs are cached until new reviews or post modifications occur. Includes an option to refresh cached summaries.
Version: 1.3
Author: Miguel Gomes
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/* ============================================================================
   SETTINGS PAGE: ChatGPT API Key & Cache Refresh Option
   ============================================================================ */

/**
 * Register plugin settings.
 */
function vre_register_settings() {
    register_setting( 'vre_settings_group', 'vre_api_key' );
}
add_action( 'admin_init', 'vre_register_settings' );

/**
 * Add plugin options page to the admin menu.
 */
function vre_add_options_page() {
    add_options_page(
        'Voxel AI Review Summary Settings',
        'Voxel AI Review Summary',
        'manage_options',
        'vre-settings',
        'vre_settings_page_html'
    );
}
add_action( 'admin_menu', 'vre_add_options_page' );

/**
 * Render the plugin settings page.
 */
function vre_settings_page_html() {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    
    // Check if cache was refreshed.
    $refreshed = isset($_GET['cache_refreshed']) ? intval($_GET['cache_refreshed']) : 0;
    ?>
    <div class="wrap">
        <h1>Voxel AI Review Summary Settings</h1>
        <form action="options.php" method="post">
            <?php
            settings_fields( 'vre_settings_group' );
            do_settings_sections( 'vre-settings' );
            ?>
            <table class="form-table">
                <tr valign="top">
                    <th scope="row">ChatGPT API Key</th>
                    <td>
                        <input type="text" name="vre_api_key" value="<?php echo esc_attr( get_option( 'vre_api_key' ) ); ?>" style="width:400px;">
                        <p class="description">Enter your OpenAI API key here.  To create a ChatGPT API Key,  <a href="https://platform.openai.com/docs/overview" target="_blank">click here</a> (Costs apply)</p>
                    </td>
                </tr>
            </table>
            <?php submit_button(); ?>
        </form>
        
        <hr>
        <h2>Cache Management</h2>
<p>Click this button to delete all cached summaries and category opinions. The plugin will then resend the request(prompt) to ChatGPT to regenerate the summaries on the next page load.
        <?php if ( $refreshed ) : ?>
            <div class="updated notice">
                <p>Cached summaries have been refreshed.</p>
            </div>
        <?php endif; ?>
        <form method="post" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>">
            <?php wp_nonce_field( 'vre_refresh_cache', 'vre_refresh_cache_nonce' ); ?>
            <input type="hidden" name="action" value="vre_refresh_cache">
            <p><input type="submit" class="button button-secondary" value="Refresh Cached Summaries"></p>
        </form>
    </div>
    <?php
}

/**
 * Handle the refresh cached summaries action.
 */
function vre_handle_refresh_cache() {
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die( 'Unauthorized user' );
    }
    if ( ! isset( $_POST['vre_refresh_cache_nonce'] ) || ! wp_verify_nonce( $_POST['vre_refresh_cache_nonce'], 'vre_refresh_cache' ) ) {
        wp_die( 'Nonce verification failed' );
    }
    global $wpdb;
    // Delete all post meta keys starting with "_vre_"
    $wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '_vre_%'" );
    
    wp_redirect( admin_url('options-general.php?page=vre-settings&cache_refreshed=1') );
    exit;
}
add_action( 'admin_post_vre_refresh_cache', 'vre_handle_refresh_cache' );

 

/* ============================================================================
   REVIEW SUMMARY SHORTCODE: [review_summary post_id=""]
   ============================================================================ */

/**
 * Shortcode: [review_summary post_id=""]
 *
 * Retrieves all reviews for a given post from the Voxel timeline table,
 * sends them to the ChatGPT API for summarization, and outputs the generated summary.
 * The summary is cached in post meta along with the review count and only updates if new reviews are added.
 *
 * The output is wrapped in a titled container for independent styling.
 */
function vre_review_summary_shortcode( $atts ) {
global $wpdb, $post;
 
$atts = shortcode_atts( array(
'post_id' => '',
), $atts, 'review_summary' );
 
// Determine the target post.
$post_id = ! empty( $atts['post_id'] ) ? intval( $atts['post_id'] ) : ( isset( $post->ID ) ? $post->ID : 0 );
if ( ! $post_id ) {
return '<p>No post specified.</p>';
}
 
// Retrieve reviews from the Voxel timeline table.
$table_name = $wpdb->prefix . 'voxel_timeline';
$query = $wpdb->prepare(
"SELECT content FROM $table_name WHERE details LIKE %s AND post_id = %d",
'%\"rating\":%',
$post_id
);
$reviews = $wpdb->get_results( $query );
 
if ( empty( $reviews ) ) {
return '<p>No reviews available for this post.</p>';
}
 
$current_review_count = count( $reviews );
 
// Check for cached summary.
$cached_summary = get_post_meta( $post_id, '_vre_review_summary', true );
$cached_count   = get_post_meta( $post_id, '_vre_review_summary_count', true );
if ( ! empty( $cached_summary ) && $cached_count == $current_review_count ) {
$summary = $cached_summary;
} else {
// Combine review texts.
$review_text = '';
foreach ( $reviews as $review ) {
$review_text .= strip_tags( $review->content ) . "\n";
}
 
// Construct prompt for ChatGPT.
$prompt = "Please provide a concise summary of the following user reviews for a listing. Focus on the main strengths and weaknesses mentioned by users, and provide a brief overview similar to what you might see on TripAdvisor.\n\nReviews:\n" . $review_text;
 
// Retrieve API key.
$api_key = get_option( 'vre_api_key', '' );
if ( empty( $api_key ) ) {
return '<p>ChatGPT API key is not configured. Please set it in the Voxel AI Review Summary settings.</p>';
}
 
$request_body = array(
'model'       => 'gpt-3.5-turbo',
'messages'    => array(
array( 'role' => 'system', 'content' => 'You are a helpful assistant that summarizes user reviews.' ),
array( 'role' => 'user', 'content'  => $prompt ),
),
'temperature' => 0.7,
);
 
$args = array(
'headers' => array(
'Content-Type'  => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
),
'body'    => json_encode( $request_body ),
'timeout' => 15,
);
 
$response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', $args );
 
if ( is_wp_error( $response ) ) {
return '<p>Error contacting ChatGPT API: ' . esc_html( $response->get_error_message() ) . '</p>';
}
 
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
 
if ( empty( $data ) || ! isset( $data['choices'][0]['message']['content'] ) ) {
return '<p>Unexpected response from ChatGPT API.</p>';
}
 
$summary = $data['choices'][0]['message']['content'];
 
// Remove any leading "Summary:" text from the returned summary.
$summary = preg_replace('/^Summary:\s*/i', '', $summary);
 
// Optionally shorten the summary further (limit to 150 words).
$words = explode(' ', $summary);
if ( count($words) > 150 ) {
$summary = implode(' ', array_slice($words, 0, 150)) . '...';
}
 
// Cache the summary.
update_post_meta( $post_id, '_vre_review_summary', $summary );
update_post_meta( $post_id, '_vre_review_summary_count', $current_review_count );
}
 
// Wrap the summary in a titled container.
$output  = '<div class="review-summary-wrapper">';
$output .= '<div class="review-summary">' . wpautop( esc_html( $summary ) ) . '</div>';
$output .= '</div>';
 
return $output;
}
add_shortcode( 'review_summary', 'vre_review_summary_shortcode' );

 

/* ============================================================================
   CATEGORY OPINIONS SHORTCODE: [category_opinions post_id="" categories="Food, Atmosphere, Service, Value"]
   ============================================================================ */

/**
 * Shortcode: [category_opinions post_id="" categories="Food, Atmosphere, Service, Value"]
 *
 * For the specified (or current) post, this shortcode generates a one-word summary for each review category.
 * You can manually specify a comma-separated list of categories.
 * Each category’s summary is generated via ChatGPT and cached until the review count changes.
 */
function vre_category_opinions_shortcode( $atts ) {
global $wpdb, $post;
 
$atts = shortcode_atts( array(
'post_id'    => '',
'categories' => 'Food, Atmosphere, Service, Value',
), $atts, 'category_opinions' );
 
// Determine target post.
$post_id = ! empty( $atts['post_id'] ) ? intval( $atts['post_id'] ) : ( isset( $post->ID ) ? $post->ID : 0 );
if ( ! $post_id ) {
return '<p>No post specified.</p>';
}
 
// Convert the categories attribute into an array.
$categories = array_map( 'trim', explode( ',', $atts['categories'] ) );
 
// Retrieve reviews.
$table_name = $wpdb->prefix . 'voxel_timeline';
$query = $wpdb->prepare(
"SELECT content FROM $table_name WHERE details LIKE %s AND post_id = %d",
'%"rating":%',
$post_id
);
$reviews = $wpdb->get_results( $query );
 
if ( empty( $reviews ) ) {
return '<p>No reviews available for this post.</p>';
}
 
$current_review_count = count( $reviews );
$opinions = array();
 
// Loop through each category.
foreach ( $categories as $cat ) {
$meta_key = '_vre_opinion_' . strtolower( preg_replace( '/\s+/', '_', $cat ) );
$count_key = $meta_key . '_review_count';
 
$cached_opinion = get_post_meta( $post_id, $meta_key, true );
$cached_count   = get_post_meta( $post_id, $count_key, true );
if ( ! empty( $cached_opinion ) && $cached_count == $current_review_count ) {
$opinions[$cat] = $cached_opinion;
continue;
}
 
// Combine review texts.
$review_text = '';
foreach ( $reviews as $review ) {
$review_text .= strip_tags( $review->content ) . "\n";
}
 
// Construct prompt for this category.
$prompt = "Based on the following user reviews for a listing, provide one word that best summarizes the overall opinion about {$cat}. Only output one word (for example, 'Delicious' or 'Mediocre').\n\nReviews:\n" . $review_text;
 
$api_key = get_option( 'vre_api_key', '' );
if ( empty( $api_key ) ) {
return '<p>ChatGPT API key is not configured. Please set it in the Voxel AI Review Summary settings.</p>';
}
 
$request_body = array(
'model'       => 'gpt-3.5-turbo',
'messages'    => array(
array( 'role' => 'system', 'content' => 'You are a helpful assistant that summarizes opinions in one word.' ),
array( 'role' => 'user', 'content'  => $prompt ),
),
'temperature' => 0.5,
);
 
$args = array(
'headers' => array(
'Content-Type'  => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
),
'body'    => json_encode( $request_body ),
'timeout' => 15,
);
 
$response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', $args );
if ( is_wp_error( $response ) ) {
$opinions[$cat] = 'Error';
continue;
}
 
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
if ( empty( $data ) || ! isset( $data['choices'][0]['message']['content'] ) ) {
$opinions[$cat] = 'N/A';
continue;
}
 
$opinion = trim( $data['choices'][0]['message']['content'] );
// Remove non-letter characters.
$opinion = preg_replace( '/[^a-zA-Z]/', '', $opinion );
$opinions[$cat] = $opinion;
 
update_post_meta( $post_id, $meta_key, $opinion );
update_post_meta( $post_id, $count_key, $current_review_count );
}
 
// Build output boxes.
$output = '<div class="vre-category-opinions" style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 5px;">';
foreach ( $categories as $cat ) {
$op = isset( $opinions[$cat] ) ? $opinions[$cat] : 'N/A';
$output .= '<div class="category-opinion-box" style="border: 1px solid #ddd; padding: 1rem; text-align: center; display: flex; flex-direction: column; justify-content: center; gap: 2px; border-radius:4px;">';
$output .= '<h4 style="margin: 0; font-size: 0.875rem; font-weight: 600;">' . esc_html( $cat ) . '</h4>';
$output .= '<p style="font-size: 0.775rem; margin: 0;">' . esc_html( $op ) . '</p>';
$output .= '</div>';
}
$output .= '</div>';
 
return $output;
}
add_shortcode( 'category_opinions', 'vre_category_opinions_shortcode' );

 

  • PHP
Copy Code

Let's Chat About this Snippet

Chat Toggle
Voxel Guide AI
Voxel Guide AI
Voxel Guide AI
Voxel Guide AI
Ask me anything about Voxel!
Send
Powered by AI24