Plugin Directory

Changeset 3428885


Ignore:
Timestamp:
12/29/2025 08:00:39 AM (3 months ago)
Author:
wpiko
Message:
  • Fixed "Access denied" errors caused by cached expired WordPress nonces
  • Added automatic nonce refresh mechanism - chatbot now seamlessly retries failed requests with fresh nonces
  • Improved resilience for sites using page caching plugins (WP Rocket, LiteSpeed, W3 Total Cache, etc.)
  • Rate-limited nonce refresh endpoint to prevent abuse
Location:
wpiko-chatbot
Files:
70 added
3 edited

Legend:

Unmodified
Added
Removed
  • wpiko-chatbot/trunk/js/wpiko-chatbot.js

    r3406566 r3428885  
    343343            hidePreMadeQuestions();
    344344
     345            // Use the retry-enabled message sending
     346            sendMessageWithRetry(message, userEmail, userName, false);
     347        }
     348    }
     349
     350    /**
     351     * Refresh the nonce from the server
     352     * @returns {Promise} Resolves with the new nonce, or rejects on error
     353     */
     354    function refreshNonce() {
     355        return new Promise(function(resolve, reject) {
    345356            jQuery.ajax({
    346357                url: wpikoChatbot.ajax_url,
    347358                type: 'post',
    348                 timeout: 90000, // 90 second timeout - gives server time to respond but prevents infinite wait
     359                timeout: 10000,
    349360                data: {
    350                     action: 'wpiko_chatbot_send_message',
    351                     message: message,
    352                     thread_id: threadId,
    353                     security: wpikoChatbot.nonce,
    354                     user_email: userEmail,
    355                     user_name: userName
     361                    action: 'wpiko_chatbot_refresh_nonce'
    356362                },
    357                 success: function (response) {
    358                     removeLoadingIndicator();
    359                     if (response.success === false) {
    360                         console.error('Error in chatbot response:', response);
    361                         if (response.data && response.data.debug) {
    362                             console.error('OpenAI debug:', response.data.debug);
     363                success: function(response) {
     364                    if (response.success && response.data && response.data.nonce) {
     365                        // Update the global nonce
     366                        wpikoChatbot.nonce = response.data.nonce;
     367                        // Update contact form nonce if available
     368                        if (response.data.contact_form_nonce) {
     369                            wpikoChatbot.contact_form_nonce = response.data.contact_form_nonce;
    363370                        }
    364                         var msg = response.data && response.data.message ? response.data.message : 'An error occurred while processing your request. Please try again later.';
    365                         // If admin-only debug is present, append status code for quicker triage (UI stays generic otherwise)
    366                         if (response.data && response.data.debug && response.data.debug.status) {
    367                             msg += ' (code: ' + response.data.debug.status + ')';
    368                         }
    369                         appendMessage('error', msg, (response.data && response.data.type) || 'general_error');
    370                         updateChatbotStatus(false);
     371                        console.log('WPiko Chatbot: Nonce refreshed successfully');
     372                        resolve(response.data.nonce);
    371373                    } else {
    372                         appendMessage('bot', response.data.response);
    373                         if (response.data.thread_id) {
    374                             threadId = response.data.thread_id;
    375                             sessionStorage.setItem('wpiko_chatbot_thread_id', threadId);
    376                         }
    377                         updateChatbotStatus(true);
     374                        reject(new Error('Invalid nonce refresh response'));
    378375                    }
    379376                },
    380                 error: function (xhr, status, error) {
    381                     console.error('AJAX error:', status, error);
    382                     console.error('Response Text:', xhr.responseText);
    383                     removeLoadingIndicator();
    384                     let errorMessage = 'An unexpected error occurred. Please try again later.';
    385                     let errorType = 'general_error';
    386 
    387                     // Handle specific error types with actionable messages
    388                     if (status === 'timeout') {
    389                         errorMessage = 'The request timed out. The server may be busy or your connection is slow. Please try again.';
    390                         errorType = 'timeout_error';
    391                     } else if (status === 'abort') {
    392                         errorMessage = 'The request was cancelled. Please try again.';
    393                         errorType = 'abort_error';
    394                     } else if (xhr.status === 0) {
    395                         errorMessage = 'Unable to connect to the server. Please check your internet connection and try again.';
    396                         errorType = 'network_error';
    397                     } else if (xhr.status === 403) {
    398                         errorMessage = 'Access denied. Please refresh the page and try again.';
    399                         errorType = 'auth_error';
    400                     } else if (xhr.status === 429) {
    401                         errorMessage = 'Too many requests. Please wait a moment before trying again.';
    402                         errorType = 'rate_limit_error';
    403                     } else if (xhr.status >= 500) {
    404                         errorMessage = 'The server encountered an error. Please try again in a few moments.';
    405                         errorType = 'server_error';
    406                     } else if (xhr.responseJSON && xhr.responseJSON.data) {
    407                         if (typeof xhr.responseJSON.data === 'object' && xhr.responseJSON.data.message) {
    408                             errorMessage = xhr.responseJSON.data.message;
    409                             errorType = xhr.responseJSON.data.type || 'general_error';
    410                         } else if (typeof xhr.responseJSON.data === 'string') {
    411                             errorMessage = xhr.responseJSON.data;
    412                         }
    413                     } else if (xhr.status !== 200 && xhr.status !== 0) {
    414                         errorMessage = 'Error: Failed to send your message. Please try again later. (Status: ' + xhr.status + ')';
    415                     }
    416 
    417                     appendMessage('error', errorMessage, errorType);
    418                     updateChatbotStatus();
    419                 },
    420                 complete: function() {
    421                     removeLoadingIndicator();
     377                error: function(xhr, status, error) {
     378                    console.error('WPiko Chatbot: Failed to refresh nonce:', error);
     379                    reject(error);
    422380                }
    423381            });
    424         }
     382        });
     383    }
     384
     385    /**
     386     * Send message with automatic retry on nonce expiration (403 error)
     387     * @param {string} message - The message to send
     388     * @param {string} userEmail - User's email
     389     * @param {string} userName - User's name
     390     * @param {boolean} isRetry - Whether this is a retry attempt
     391     */
     392    function sendMessageWithRetry(message, userEmail, userName, isRetry) {
     393        jQuery.ajax({
     394            url: wpikoChatbot.ajax_url,
     395            type: 'post',
     396            timeout: 90000, // 90 second timeout - gives server time to respond but prevents infinite wait
     397            data: {
     398                action: 'wpiko_chatbot_send_message',
     399                message: message,
     400                thread_id: threadId,
     401                security: wpikoChatbot.nonce,
     402                user_email: userEmail,
     403                user_name: userName
     404            },
     405            success: function (response) {
     406                removeLoadingIndicator();
     407                if (response.success === false) {
     408                    console.error('Error in chatbot response:', response);
     409                    if (response.data && response.data.debug) {
     410                        console.error('OpenAI debug:', response.data.debug);
     411                    }
     412                    var msg = response.data && response.data.message ? response.data.message : 'An error occurred while processing your request. Please try again later.';
     413                    // If admin-only debug is present, append status code for quicker triage (UI stays generic otherwise)
     414                    if (response.data && response.data.debug && response.data.debug.status) {
     415                        msg += ' (code: ' + response.data.debug.status + ')';
     416                    }
     417                    appendMessage('error', msg, (response.data && response.data.type) || 'general_error');
     418                    updateChatbotStatus(false);
     419                } else {
     420                    appendMessage('bot', response.data.response);
     421                    if (response.data.thread_id) {
     422                        threadId = response.data.thread_id;
     423                        sessionStorage.setItem('wpiko_chatbot_thread_id', threadId);
     424                    }
     425                    updateChatbotStatus(true);
     426                }
     427            },
     428            error: function (xhr, status, error) {
     429                // Check if this is a 403 error (nonce expired) and we haven't retried yet
     430                if (xhr.status === 403 && !isRetry) {
     431                    console.log('WPiko Chatbot: Nonce may be expired, attempting to refresh...');
     432                   
     433                    // Try to refresh the nonce and retry the request
     434                    refreshNonce()
     435                        .then(function() {
     436                            console.log('WPiko Chatbot: Retrying message with new nonce...');
     437                            sendMessageWithRetry(message, userEmail, userName, true);
     438                        })
     439                        .catch(function() {
     440                            // Nonce refresh failed, show error to user
     441                            removeLoadingIndicator();
     442                            appendMessage('error', 'Session expired. Please refresh the page and try again.', 'auth_error');
     443                            updateChatbotStatus();
     444                        });
     445                    return;
     446                }
     447
     448                console.error('AJAX error:', status, error);
     449                console.error('Response Text:', xhr.responseText);
     450                removeLoadingIndicator();
     451                let errorMessage = 'An unexpected error occurred. Please try again later.';
     452                let errorType = 'general_error';
     453
     454                // Handle specific error types with actionable messages
     455                if (status === 'timeout') {
     456                    errorMessage = 'The request timed out. The server may be busy or your connection is slow. Please try again.';
     457                    errorType = 'timeout_error';
     458                } else if (status === 'abort') {
     459                    errorMessage = 'The request was cancelled. Please try again.';
     460                    errorType = 'abort_error';
     461                } else if (xhr.status === 0) {
     462                    errorMessage = 'Unable to connect to the server. Please check your internet connection and try again.';
     463                    errorType = 'network_error';
     464                } else if (xhr.status === 403) {
     465                    // This only triggers if retry also failed
     466                    errorMessage = 'Access denied. Please refresh the page and try again.';
     467                    errorType = 'auth_error';
     468                } else if (xhr.status === 429) {
     469                    errorMessage = 'Too many requests. Please wait a moment before trying again.';
     470                    errorType = 'rate_limit_error';
     471                } else if (xhr.status >= 500) {
     472                    errorMessage = 'The server encountered an error. Please try again in a few moments.';
     473                    errorType = 'server_error';
     474                } else if (xhr.responseJSON && xhr.responseJSON.data) {
     475                    if (typeof xhr.responseJSON.data === 'object' && xhr.responseJSON.data.message) {
     476                        errorMessage = xhr.responseJSON.data.message;
     477                        errorType = xhr.responseJSON.data.type || 'general_error';
     478                    } else if (typeof xhr.responseJSON.data === 'string') {
     479                        errorMessage = xhr.responseJSON.data;
     480                    }
     481                } else if (xhr.status !== 200 && xhr.status !== 0) {
     482                    errorMessage = 'Error: Failed to send your message. Please try again later. (Status: ' + xhr.status + ')';
     483                }
     484
     485                appendMessage('error', errorMessage, errorType);
     486                updateChatbotStatus();
     487            },
     488            complete: function() {
     489                removeLoadingIndicator();
     490            }
     491        });
    425492    }
    426493
  • wpiko-chatbot/trunk/readme.txt

    r3426080 r3428885  
    44Requires at least: 6.0
    55Tested up to: 6.9
    6 Stable tag: 1.0.8
     6Stable tag: 1.0.9
    77Requires PHP: 7.0
    88License: GPL-2.0+
     
    1313== Description ==
    1414
    15 WPiko AI Chatbot brings OpenAI’s latest models to your WordPress site for instant, accurate answers, lead capture, and automated support—without monthly fees. It’s fast, flexible, and designed for real business outcomes like resolving FAQs, assisting shoppers, and qualifying leads.
     15WPiko AI Chatbot brings OpenAI's latest models to your WordPress site for instant, accurate answers, lead capture, and automated support—without monthly fees. It's fast, flexible, and designed for real business outcomes like resolving FAQs, assisting shoppers, and qualifying leads.
    1616
    1717https://www.youtube.com/watch?v=CCgrX_wZCg4
     
    5353### 📱 Mobile-Friendly
    5454
    55 WPikos chatbot adapts seamlessly to phones, tablets, and desktops. Touch‑friendly buttons, adjustable dimensions, and compatibility with common themes and page builders ensure a smooth experience without covering key UI elements. The admin includes a mobile‑friendly navigation.
     55WPiko's chatbot adapts seamlessly to phones, tablets, and desktops. Touch‑friendly buttons, adjustable dimensions, and compatibility with common themes and page builders ensure a smooth experience without covering key UI elements. The admin includes a mobile‑friendly navigation.
    5656
    5757### ✅ Get Started with WPiko Chatbot Today
     
    1081081. Engage visitors with a sleek, customizable chat interface that matches your brand and provides instant responses to common questions.
    1091092. Easily configure your chatbot's personality, knowledge base, and behavior through an intuitive AI training interface powered by OpenAI technology.
    110 3. Review and analyze conversations to understand customer needs, improve your AIs responses, and refine your support strategy.
     1103. Review and analyze conversations to understand customer needs, improve your AI's responses, and refine your support strategy.
    1111114. Transform your chatbot into a direct communication channel with the built-in contact form. Allow visitors to reach out without leaving the chat interface.
    1121125. Gain valuable visitor insights through detailed analytics including conversation metrics, geographic data, and user engagement patterns.
    113113
    114114== Changelog ==
     115
     116= 1.0.9 =
     117* Fixed "Access denied" errors caused by cached expired WordPress nonces
     118* Added automatic nonce refresh mechanism - chatbot now seamlessly retries failed requests with fresh nonces
     119* Improved resilience for sites using page caching plugins (WP Rocket, LiteSpeed, W3 Total Cache, etc.)
     120* Rate-limited nonce refresh endpoint to prevent abuse
    115121
    116122= 1.0.8 =
  • wpiko-chatbot/trunk/wpiko-chatbot.php

    r3426080 r3428885  
    44 * Plugin URI: https://wpiko.com/chatbot
    55 * Description: A WordPress plugin that integrates OpenAI's AI models to create an intelligent chatbot for WordPress websites.
    6  * Version: 1.0.8
     6 * Version: 1.0.9
    77 * Author: WPiko
    88 * Author URI: https://wpiko.com
     
    2020define('WPIKO_CHATBOT_PLUGIN_DIR', plugin_dir_path(__FILE__));
    2121define('WPIKO_CHATBOT_PLUGIN_URL', plugin_dir_url(__FILE__));
    22 define('WPIKO_CHATBOT_VERSION', '1.0.8');
     22define('WPIKO_CHATBOT_VERSION', '1.0.9');
    2323
    2424// Ensures that the default options is set
     
    127127require_once WPIKO_CHATBOT_PLUGIN_DIR . 'includes/instructions-handler.php';
    128128require_once WPIKO_CHATBOT_PLUGIN_DIR . 'includes/cache-management.php';
     129require_once WPIKO_CHATBOT_PLUGIN_DIR . 'includes/nonce-refresh.php';
    129130require_once WPIKO_CHATBOT_PLUGIN_DIR . 'includes/conversation-translation.php';
    130131
Note: See TracChangeset for help on using the changeset viewer.