# mxchat-basic/1.0.5/includes/class-mxchat-integrator.php

MxChat – AI Chatbot &amp; Content Generation for WordPress, version 1.0.5. 434 lines.

- Page: https://pluginprobe.com/plugins/mxchat-basic/1.0.5/code/includes/class-mxchat-integrator.php
- Raw: https://pluginprobe.com/plugins/mxchat-basic/1.0.5/raw/includes/class-mxchat-integrator.php
- Modified: 2024-08-19T03:17:02+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/mxchat-basic/1.0.5/code/includes/class-mxchat-integrator.php#L10-L20`.

```php
<?php
if (!defined('ABSPATH')) {
    exit;
}

class MxChat_Integrator {
    private $options;
    private $chat_count;

    public function __construct() {
        $this->options = get_option('mxchat_options');
        $this->chat_count = get_option('mxchat_chat_count', 0);

        add_action('wp_enqueue_scripts', array($this, 'mxchat_enqueue_scripts_styles'));
        add_action('wp_ajax_mxchat_handle_chat_request', array($this, 'mxchat_handle_chat_request'));
        add_action('wp_ajax_nopriv_mxchat_handle_chat_request', array($this, 'mxchat_handle_chat_request'));
        add_action('wp_ajax_mxchat_dismiss_pre_chat_message', array($this, 'mxchat_dismiss_pre_chat_message'));
        add_action('wp_ajax_nopriv_mxchat_dismiss_pre_chat_message', array($this, 'mxchat_dismiss_pre_chat_message'));


        if (!wp_next_scheduled('mxchat_reset_rate_limits')) {
            wp_schedule_event(time(), 'daily', 'mxchat_reset_rate_limits');
        }

        add_action('mxchat_reset_rate_limits', array($this, 'mxchat_reset_rate_limits'));
    }

    private function mxchat_increment_chat_count() {
        $chat_count = get_option('mxchat_chat_count', 0);
        $chat_count++;
        update_option('mxchat_chat_count', $chat_count);
    }
    
public function mxchat_fetch_conversation_history_for_ajax($session_id) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';

    // Prepare and execute the query safely
    $chat_transcripts = $wpdb->get_results(
        $wpdb->prepare("SELECT * FROM $table_name WHERE session_id = %s ORDER BY timestamp ASC", sanitize_text_field($session_id))
    );

    // Check if results are empty
    if (empty($chat_transcripts)) {
        return [];
    }

    // Build the conversation history
    $conversation_history = [];
    foreach ($chat_transcripts as $transcript) {
        $conversation_history[] = [
            'role' => $transcript->role,
            'content' => $transcript->message
        ];
    }

    return $conversation_history;
}
  
    
    private function mxchat_save_chat_message($session_id, $role, $message) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';

        $wpdb->insert($table_name, [
        'user_id' => 0,
        'session_id' => $session_id,
        'role' => $role,
        'message' => $message,
        'timestamp' => current_time('mysql', 1) 
    ]);
}



public function mxchat_handle_chat_request() {
    global $wpdb;

    // Get and sanitize the user identifier
    $user_id = $this->mxchat_get_user_identifier();
    $user_id = sanitize_key($user_id);

    // Manage rate limiting
    $rate_limit_transient_key = 'mxchat_chat_limit_' . $user_id;
    $chat_count = get_transient($rate_limit_transient_key);
    $session_transient_key = 'mxchat_chat_session_' . $user_id;
    $session_id = get_transient($session_transient_key);

    if ($chat_count === false) {
        $chat_count = 0;
    }

    if ($session_id === false) {
        $session_id = uniqid('mxchat_chat_', true);
        set_transient($session_transient_key, $session_id, DAY_IN_SECONDS); // Store session ID for a day
    }

    $rate_limit_option = isset($this->options['rate_limit']) ? $this->options['rate_limit'] : 'unlimited';

    // Check if rate limit is not 'unlimited'
    if ($rate_limit_option !== 'unlimited') {
        $rate_limit = intval($rate_limit_option);

        if ($chat_count >= $rate_limit) {
            wp_send_json_error('Rate limit exceeded. Please try again later.');
            wp_die();
        }
        set_transient($rate_limit_transient_key, $chat_count + 1, DAY_IN_SECONDS);
    }

    // Validate and sanitize the incoming message
    if (!isset($_POST['message'])) {
        wp_send_json_error('No message received');
        wp_die();
    }

    $message = sanitize_text_field($_POST['message']);
    if (empty($message)) {
        wp_send_json_error('Message is empty or invalid.');
        wp_die();
    }

    // Save the user message to the database
    $this->mxchat_save_chat_message($session_id, 'user', $message);

    // Generate and validate the embedding
    $user_message_embedding = $this->mxchat_generate_embedding($message, $this->options['api_key']);
    if (!is_array($user_message_embedding)) {
        wp_send_json_error('Error processing your message.');
        wp_die();
    }

    // Find relevant content based on embedding
    $relevant_content = $this->mxchat_find_relevant_content($user_message_embedding);

    // Fetch conversation history from the database
    $conversation_history = $this->mxchat_fetch_conversation_history_for_ajax($session_id);

    // Increment the chat count
    $this->mxchat_increment_chat_count();

    // Generate a response from the AI model
    $response = $this->mxchat_generate_response($relevant_content, $this->options['api_key'], $conversation_history);

    // Save the bot response to the database
    $this->mxchat_save_chat_message($session_id, 'bot', $response);

    // Send the response back to the client
    wp_send_json(['message' => $response]);

    wp_die();
}


    private function mxchat_get_user_identifier() {
        return sanitize_text_field($_SERVER['REMOTE_ADDR']);
    }



    private function mxchat_generate_embedding($text, $api_key) {
        $endpoint = 'https://api.openai.com/v1/embeddings';

        $body = wp_json_encode([
            'input' => $text,
            'model' => 'text-embedding-ada-002'
        ]);

        $args = [
            'body' => $body,
            'headers' => [
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . $api_key,
            ],
            'timeout' => 60,
            'redirection' => 5,
            'blocking' => true,
            'httpversion' => '1.0',
            'sslverify' => true,
        ];

        $response = wp_remote_post($endpoint, $args);

        if (is_wp_error($response)) {
            return null;
        }

        $response_body = json_decode(wp_remote_retrieve_body($response), true);

        if (isset($response_body['data'][0]['embedding']) && is_array($response_body['data'][0]['embedding'])) {
            return $response_body['data'][0]['embedding'];
        } else {
            return null;
        }
    }

    private function mxchat_find_relevant_content($user_embedding) {
        global $wpdb;
        $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';

        // Define a cache key for embeddings
        $cache_key = 'mxchat_system_prompt_embeddings';

        // Attempt to get the embeddings from the cache
        $embeddings = wp_cache_get($cache_key, 'mxchat_system_prompts');

        if ($embeddings === false) {
            // Cache miss, query the database and cache the results
            $query = "SELECT id, embedding_vector FROM {$system_prompt_table}";
            $embeddings = $wpdb->get_results($query);

            if ($embeddings === null || empty($embeddings)) {
                error_log("No embeddings found in the database.");
                return null; // Return null to handle no embeddings gracefully
            }

            // Cache the results if successful
            wp_cache_set($cache_key, $embeddings, 'mxchat_system_prompts', 3600); // Cache for 1 hour
        }

        $most_relevant_id = null;
        $highest_similarity = -INF;

        foreach ($embeddings as $embedding) {
            $database_embedding = maybe_unserialize($embedding->embedding_vector);

            // Debugging: Log the embeddings
            if (!is_array($database_embedding)) {
                error_log("Invalid database embedding format for ID {$embedding->id}: " . print_r($database_embedding, true));
                continue;
            }

            if (is_array($user_embedding)) {
                $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $database_embedding);

                // Debugging: Log the similarity score
                error_log("Calculated similarity for ID {$embedding->id}: {$similarity}");

                if ($similarity > $highest_similarity) {
                    $highest_similarity = $similarity;
                    $most_relevant_id = $embedding->id;
                }
            } else {
                error_log("User embedding is not an array. Embedding data: " . print_r($user_embedding, true));
            }
        }

        if ($most_relevant_id !== null) {
            // Define a cache key for the relevant content
            $content_cache_key = 'mxchat_article_content_' . $most_relevant_id;

            // Attempt to get the relevant content from the cache
            $relevant_content = wp_cache_get($content_cache_key, 'mxchat_system_prompts');

            if ($relevant_content === false) {
                // Cache miss, query the database and cache the result
                $query = $wpdb->prepare("SELECT article_content FROM {$system_prompt_table} WHERE id = %d", $most_relevant_id);
                $relevant_content = $wpdb->get_var($query);

                if ($relevant_content === null) {
                    error_log("No relevant content found for ID {$most_relevant_id}.");
                    return null; // Return null if no content is found
                }

                wp_cache_set($content_cache_key, $relevant_content, 'mxchat_system_prompts', 3600); // Cache for 1 hour
            }

            return $relevant_content;
        }

        error_log("No relevant content found. Most relevant ID was null.");
        return null; // Return null if no relevant content is found
    }

    private function mxchat_generate_response($relevant_content, $api_key, $conversation_history) {
    if (!$relevant_content) {
        return "I'm sorry, I couldn't find relevant information on that topic.";
    }

    $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';

    $content_with_instructions = $system_prompt_instructions . " " . $relevant_content;

    array_unshift($conversation_history, [
        'role' => 'system', 
        'content' => "Here are your instructions: " . $content_with_instructions
    ]);

    foreach ($conversation_history as &$message) {
        if ($message['role'] === 'bot') {
            $message['role'] = 'assistant';
        }
    }

    $api_url = 'https://api.openai.com/v1/chat/completions';

    $body = json_encode([
        'model' => isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5',
        'messages' => $conversation_history,
    ]);
    
    $args = [
        'body'        => $body,
        'headers'     => [
            'Content-Type' => 'application/json',
            'Authorization' => 'Bearer ' . $api_key,
        ],
        'timeout'     => 60,
        'redirection' => 5,
        'blocking'    => true,
        'httpversion' => '1.0',
        'sslverify'   => true,
    ];

    $response = wp_remote_post($api_url, $args);

    if (is_wp_error($response)) {
        return "Sorry, there was an error processing your request.";
    }

    $response_body = json_decode(wp_remote_retrieve_body($response), true);
    
    if (isset($response_body['choices'][0]['message']['content'])) {
        if (isset($response_body['usage'])) {
            $prompt_tokens = $response_body['usage']['prompt_tokens'];
            $total_tokens = $response_body['usage']['total_tokens'];
        }
        return trim($response_body['choices'][0]['message']['content']);
    } else {
        return "Sorry, I couldn't process that request.";
    }
}


public function mxchat_dismiss_pre_chat_message() {
    // Get and sanitize the user identifier
    $user_id = $this->mxchat_get_user_identifier();
    $user_id = sanitize_key($user_id);

    // Set a transient to track that the user has dismissed the pre-chat message
    $transient_key = 'mxchat_pre_chat_message_dismissed_' . $user_id;
    set_transient($transient_key, true, DAY_IN_SECONDS);

    wp_send_json_success();
}



    private function mxchat_calculate_cosine_similarity($vectorA, $vectorB) {
        if (!is_array($vectorA) || !is_array($vectorB) || empty($vectorA) || empty($vectorB)) {
            return 0;
        }

        $dotProduct = array_sum(array_map(function ($a, $b) {
            return $a * $b;
        }, $vectorA, $vectorB));
        $normA = sqrt(array_sum(array_map(function ($a) {
            return $a * $a;
        }, $vectorA)));
        $normB = sqrt(array_sum(array_map(function ($b) {
            return $b * $b;
        }, $vectorB)));

        if ($normA == 0 || $normB == 0) {
            return 0;
        }

        return $dotProduct / ($normA * $normB);
    }

    public function mxchat_enqueue_scripts_styles() {
        // Define version numbers for the styles and scripts
        $chat_style_version = '1.0.5'; // Replace with your actual version
        $chat_script_version = '1.0.5'; // Replace with your actual version
    
        // Correct path to the script file
        wp_enqueue_script(
            'mxchat-chat-js', // Handle for the script
            plugin_dir_url(__FILE__) . '../js/chat-script.js', // Correct path using __FILE__
            array('jquery'), // Dependencies
            $chat_script_version, // Version for cache busting
            true // Load script in footer
        );
    
        // Enqueue the CSS file similarly
        wp_enqueue_style(
            'mxchat-chat-css', // Handle for the style
            plugin_dir_url(__FILE__) . '../css/chat-style.css', // Correct path using __FILE__
            array(), // No dependencies
            $chat_style_version // Version for cache busting
        );
    
        // Fetch options from the database
        $this->options = get_option('mxchat_options');
    
        // Prepare settings to pass to JavaScript
        $style_settings = array(
            'ajax_url' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('mxchat_chat_nonce'), // Nonce for security
            'rate_limit_message' => 'Rate limit exceeded. Please try again later.',
            'appendWidgetToBody' => $this->options['append_to_body'] ?? 'off'
        );
    
        // Localize the script with necessary data
        wp_localize_script('mxchat-chat-js', 'mxchatChat', $style_settings);
    }



    public function mxchat_reset_rate_limits() {
        global $wpdb;

        // Define a cache key pattern for rate limits
        $cache_key_pattern = 'mxchat_chat_limit_%';

        // Retrieve all option names matching the pattern
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
        $option_names = $wpdb->get_col("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE 'mxchat_chat_limit_%'");

        // db call ok; no-cache ok
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- db call ok
        $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE 'mxchat_chat_limit_%'");

        // Clear the relevant cache entries
        foreach ($option_names as $option_name) {
            wp_cache_delete($option_name, 'options');
        }

        // Optionally, clear a general cache if you have one
        wp_cache_delete('mxchat_all_chat_limits', 'options');
    }
}
?>

```
