* Author URI: https://aisk.chat/ * Contributors: aisk, zrshishir, sabbirxprt * Text Domain: aisk-ai-chat * Domain Path: /languages/ * License: GPLv2 or later * License URI: https://www.gnu.org/licenses/gpl-2.0.html * * @category WordPress * @package AISK * @author Aisk Team * @license GPL-2.0+ http://www.gnu.org/licenses/gpl-2.0.txt * @link https://aisk.chat * * Third-party Libraries: * - Smalot/PdfParser: Required for PDF text extraction and processing */ if ( ! defined('ABSPATH') ) { exit; } // Load Composer autoloader if available if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) { require_once __DIR__ . '/vendor/autoload.php'; } else { // Log error but don't stop plugin execution if ( defined('WP_DEBUG') && WP_DEBUG ) { // error_log('Aisk AI Chat: Composer autoloader not found. Some features may not work properly.'); } } /** * Main plugin class for AISK AI Chatbot * * Handles initialization, dependencies loading, and core functionality * of the AISK chatbot plugin for WordPress and WooCommerce. * * @category WordPress * @package AISK * @author Aisk Team * @license GPL-2.0+ http://www.gnu.org/licenses/gpl-2.0.txt * @link https://aisk.chat */ class AISK_AI_Chatbot { private static $instance = null; private $pdf_queue_handler = null; /** * Get singleton instance of this class * * @return AISK_AI_Chatbot Instance of this class */ public static function get_instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * Constructor for initializing the plugin */ private function __construct() { // Define constants define('AISK_PLUGIN_FILE', __FILE__); define('AISK_VERSION', '2.7.3'); define('AISK_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('AISK_PLUGIN_URL', plugin_dir_url(__FILE__)); define('AISK_TEXT_DOMAIN', 'aisk-ai-chat'); define('AISK_CHAT_SESSION_COOKIE', 'aisk_chat_session'); define('AISK_GREAP_KEY', 'zcP5AsW6bwGApWF5NdyF4TvyKZs6w7KP'); define('AISK_APPSERO_TOKEN', 'e46991ff-ae9b-42f3-bf87-1ec87ab8bb77'); define('AISK_OPENAI_API_URL', 'https://api.openai.com/v1'); // Initialize components add_action('init', [ $this, 'init' ]); add_action('init', [ $this, 'aisk_increase_upload_limits' ]); register_activation_hook(__FILE__, [ $this, 'activate' ]); // Ensure API Usage table exists on upgrade to 2.5.0+ // Runs on every load but only acts when needed add_action('plugins_loaded', [ $this, 'ensure_api_usage_table_on_upgrade' ], 1); $this->include_appsero_client(); $this->appsero_init_tracker_aisk_ai_chat(); // Load required files $this->load_dependencies(); // Initialize PDF queue handler $this->init_pdf_queue_handler(); add_action( 'admin_enqueue_scripts', [ $this, 'gleap_widget_script' ] ); } /** * Generate Gleap inline script for admin * * @param WP_User $user * @param string $user_role * @return string */ private function get_gleap_inline_script( $user, $user_role ) { return wp_sprintf( '!function(Gleap,t,i){ if(!(Gleap=window.Gleap=window.Gleap||[]).invoked){for(window.GleapActions=[],Gleap.invoked=!0,Gleap.methods=["identify","setEnvironment","setTicketAttribute","setTags","attachCustomData","setCustomData","removeCustomData","clearCustomData","registerCustomAction","trackEvent","setUseCookies","log","preFillForm","showSurvey","sendSilentCrashReport","startFeedbackFlow","startBot","setAppBuildNumber","setAppVersionCode","setApiUrl","setFrameUrl","isOpened","open","close","on","setLanguage","setOfflineMode","startClassicForm","initialize","disableConsoleLogOverwrite","logEvent","hide","enableShortcuts","showFeedbackButton","destroy","getIdentity","isUserIdentified","clearIdentity","openConversations","openConversation","openHelpCenterCollection","openHelpCenterArticle","openHelpCenter","searchHelpCenter","openNewsArticle","openChecklists","startChecklist","openNews","openFeatureRequests","isLiveMode"],Gleap.f=function(e){return function(){var t=Array.prototype.slice.call(arguments);window.GleapActions.push({e:e,a:t})}},t=0;tID : $user->ID ), esc_js( $user->user_email ), esc_js( $user->display_name ), esc_js( $user_role ), esc_js( defined('AISK_VERSION') ? AISK_VERSION : '' ), esc_js( function_exists('get_site_url') ? get_site_url() : '' ), esc_js( function_exists('is_multisite') && is_multisite() ? 'Yes' : 'No' ), esc_js( function_exists('get_bloginfo') ? get_bloginfo('version') : '' ), esc_js( defined('PHP_VERSION') ? PHP_VERSION : '' ), esc_js( function_exists('get_bloginfo') ? get_bloginfo('name') : '' ), esc_js( defined('AISK_GREAP_KEY') ? AISK_GREAP_KEY : '' ) ); } /** * Gleap widget script * * @return void */ public function gleap_widget_script() { // Only load on stock notifier plugin pages $screen = get_current_screen(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check of which admin page is open $current_page = isset($_GET['page']) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // Check if we're on stock notifier pages if ( (!$screen || strpos($screen->base, 'aisk') === false) && $current_page !== 'aisk-inquiries' && $current_page !== 'aisk-history' && $current_page !== 'aisk-uses' && $current_page !== 'aisk-settings' ) { return; } $user = wp_get_current_user(); $user_role = ( !empty($user->roles) && isset($user->roles[0]) ) ? $user->roles[0] : ''; wp_register_script( 'aisk-gleap-dummy', false, [], AISK_VERSION, true ); wp_enqueue_script( 'aisk-gleap-dummy' ); wp_add_inline_script( 'aisk-gleap-dummy', $this->get_gleap_inline_script( $user, $user_role ) ); } /** * Initialize the plugin tracker * * @return void */ public function appsero_init_tracker_aisk_ai_chat() { $client = new Aisk_Ai_Chat\Appsero\Client( AISK_APPSERO_TOKEN, 'Aisk — AI Sales Chatbot for WooCommerce | Knowledgebase & Support bot', AISK_PLUGIN_FILE ); // Active insights. $client->insights()->init(); } /** * Includes the Appsero client by creating a class alias if necessary. * * If the 'Appsero\Client' class is not defined, it will create an alias to * 'AISK_AI_CHAT\Appsero\Client'. * * @version 1.0.0 * @return void */ public function include_appsero_client() { if ( ! class_exists( 'Aisk_Ai_Chat\Appsero\Client' ) ) { require_once __DIR__ . '/appsero/client/src/Client.php'; } if ( ! class_exists('Aisk_Ai_Chat\Appsero\Client') ) { class_alias('Aisk_Ai_Chat\Appsero\Client','Aisk_Ai_Chat\Appsero\Client'); } } /** * Initialize the PDF queue handler */ private function init_pdf_queue_handler() { $this->pdf_queue_handler = new AISK_PDF_Queue_Handler(); add_action('wp_loaded', [ $this, 'handle_pdf_queue_processing' ]); } /** * Handle PDF queue processing requests */ public function handle_pdf_queue_processing() { // Check for real cron request and verify nonce $cron = isset($_GET['aisk_cron']) ? sanitize_key(wp_unslash($_GET['aisk_cron'])) : ''; if ('process_pdf_queue' !== $cron || !isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'aisk_pdf_queue')) { return; } // Verify the security key $key = isset($_GET['key']) ? sanitize_text_field(wp_unslash($_GET['key'])) : ''; if ('' === $key || !hash_equals(wp_hash('aisk_pdf_queue'), $key)) { wp_die('Invalid security key', 'Security Error', array('response' => 403)); } // Process the queue $processed = $this->pdf_queue_handler->process_pending_jobs(); // Return JSON response wp_send_json(array( 'success' => true, 'processed' => $processed, 'timestamp' => current_time('mysql') )); } function aisk_increase_upload_limits() { // Increase PHP limits using WordPress filters instead of ini_set add_filter('upload_max_filesize', function() { return '128M'; }); add_filter('post_max_size', function() { return '128M'; }); add_filter('memory_limit', function() { return '512M'; }); add_filter('max_execution_time', function() { return 600; }); // 10 minutes add_filter('max_input_time', function() { return 600; }); // Increase WordPress limits add_filter('upload_size_limit', function($size) { return 64 * 1024 * 1024; // 64MB }); // Try to modify .htaccess if we have access if (is_admin() && current_user_can('manage_options')) { global $wp_filesystem; if (empty($wp_filesystem)) { require_once(ABSPATH . '/wp-admin/includes/file.php'); WP_Filesystem(); } $htaccess_path = ABSPATH . '.htaccess'; if ($wp_filesystem->is_writable($htaccess_path)) { $htaccess_content = $wp_filesystem->get_contents($htaccess_path); // Check if our rules already exist if (strpos($htaccess_content, '# BEGIN AISK Upload Limits') === false) { $upload_rules = " # BEGIN AISK Upload Limits php_value upload_max_filesize 128M php_value post_max_size 128M php_value memory_limit 512M php_value max_execution_time 600 php_value max_input_time 600 php_value upload_max_filesize 128M php_value post_max_size 128M php_value memory_limit 512M php_value max_execution_time 600 php_value max_input_time 600 # END AISK Upload Limits "; // Add our rules after the WordPress rules $htaccess_content = preg_replace('/(# BEGIN WordPress[\s\S]+?# END WordPress)/', '$1' . "\n" . $upload_rules, $htaccess_content); file_put_contents($htaccess_path, $htaccess_content); } } } } /** * Initialize plugin hooks and features * * @return void */ public function init() { // Loads the translations bundled in /languages (translate.wordpress.org packs load automatically) // phpcs:ignore PluginCheck.CodeAnalysis.DiscouragedFunctions.load_plugin_textdomainFound load_plugin_textdomain( 'aisk-ai-chat', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); // Enqueue scripts add_action('wp_enqueue_scripts', [ $this, 'enqueue_scripts' ]); // Add chat widget to footer add_action('wp_footer', [ $this, 'render_chat_widget' ]); $this->maybe_load_integrations(); // Initialize contact form handler if enabled add_action('wp_loaded', [ 'AISK_Contact_Form_Handler', 'maybe_init' ]); } /** * Load required plugin files and dependencies * * @return void */ private function load_dependencies() { include_once AISK_PLUGIN_DIR . 'includes/class-database.php'; include_once AISK_PLUGIN_DIR . 'includes/class-api-usage-tracker.php'; include_once AISK_PLUGIN_DIR . 'includes/class-model-manager.php'; include_once AISK_PLUGIN_DIR . 'includes/class-help-questions.php'; include_once AISK_PLUGIN_DIR . 'includes/class-embeddings-handler.php'; include_once AISK_PLUGIN_DIR . 'includes/class-external-embeddings-handler.php'; include_once AISK_PLUGIN_DIR . 'includes/class-product-handler.php'; include_once AISK_PLUGIN_DIR . 'includes/class-order-handler.php'; include_once AISK_PLUGIN_DIR . 'includes/class-chat-storage.php'; include_once AISK_PLUGIN_DIR . 'includes/class-response-formatter.php'; include_once AISK_PLUGIN_DIR . 'includes/class-chat-handler.php'; include_once AISK_PLUGIN_DIR . 'includes/class-script-loader.php'; include_once AISK_PLUGIN_DIR . 'includes/class-aisk-admin.php'; include_once AISK_PLUGIN_DIR . 'includes/services/queue/class-pdf-queue-handler.php'; // Initialize admin class if (is_admin()) { add_action('admin_init', function() { AISK_Admin::get_instance(); }); } // Load contact form feature handlers include_once AISK_PLUGIN_DIR . 'includes/features/class-contact-form-handler.php'; // Ensure database tables exist even after updates (without reactivation) // Safe to call: dbDelta is idempotent try { new AISK_Database(); } catch ( \Throwable $e ) {} // Help questions REST routes (generated from Contact Information, Custom Content & indexed content) AISK_Help_Questions::get_instance(); // Initialize PDF Queue Handler new AISK_PDF_Queue_Handler(); } /** * Ensure `aisk_api_usage` exists when upgrading to >= 2.5.0 * Mirrors the user's version-gated pattern * * @return void */ public function ensure_api_usage_table_on_upgrade() { global $wpdb; $stored_version = get_option('aisk_plugin_version', '1.0.0'); $current_version = defined('AISK_VERSION') ? AISK_VERSION : '0.0.0'; $stored_num = floatval(str_replace('.', '', $stored_version)); $current_num = floatval(str_replace('.', '', $current_version)); $threshold_num = floatval(str_replace('.', '', '2.5.0')); // Only act when upgrading to >= 2.5.0 from a lower version if ($stored_num < $threshold_num && $current_num >= $threshold_num) { $table_name = $wpdb->prefix . 'aisk_api_usage'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Table existence check during upgrade needs real-time data $exists = $wpdb->get_var( $wpdb->prepare('SHOW TABLES LIKE %s', $table_name) ); if ($exists !== $table_name) { $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE IF NOT EXISTS {$table_name} ( id bigint(20) NOT NULL AUTO_INCREMENT, request_id varchar(100) NOT NULL, conversation_id varchar(50), user_id bigint(20), user_role varchar(50), channel varchar(50) NOT NULL, feature varchar(50) NOT NULL, provider varchar(50) NOT NULL, endpoint varchar(255), model varchar(100), status varchar(20) NOT NULL, error_code varchar(100), latency_ms int(11), tokens_in int(11) DEFAULT 0, tokens_out int(11) DEFAULT 0, cost_usd decimal(10,4) DEFAULT 0.0000, ip_hash varchar(64), country varchar(100), url varchar(255), context_id varchar(100), metadata JSON, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY request_id (request_id), KEY conversation_id (conversation_id), KEY user_id (user_id), KEY channel (channel), KEY feature (feature), KEY provider (provider), KEY status (status), KEY created_at (created_at), KEY provider_feature (provider, feature), KEY created_at_provider (created_at, provider), KEY created_at_feature (created_at, feature) ) {$charset_collate};"; include_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta($sql); } } } /** * Load integration modules if enabled in settings * * @return void */ private function maybe_load_integrations() { $settings = get_option('aisk_settings', []); // Load WhatsApp class if enabled if ( ! empty($settings['integrations']['whatsapp']['enabled']) ) { include_once AISK_PLUGIN_DIR . 'includes/messenger/class-whatsapp-handler.php'; new AISK_WhatsApp_Handler(); } // Load Telegram class if enabled if ( ! empty($settings['integrations']['telegram']['enabled']) ) { include_once AISK_PLUGIN_DIR . 'includes/messenger/class-telegram-handler.php'; new AISK_Telegram_Handler(); } } /** * Handle plugin activation tasks * * @return void */ public function activate() { // Initialize contact form if enabled $contact_form = AISK_Contact_Form_Handler::get_instance(); if ( $contact_form->is_enabled() ) { $contact_form->ensure_page_and_template_exists(); } new AISK_Database(); } /** * Enqueue required frontend scripts and styles * * @return void */ public function enqueue_scripts() { if ( ! is_page('aisk-contact-form') ) { AISK_Scripts::load_chat_widget_assets(); } } /** * Render the chat widget container in footer * * @return void */ public function render_chat_widget() { // Check if we're on the contact form page if ( ! is_page('aisk-contact-form') ) { echo '
'; } } } // Initialize the plugin AISK_AI_Chatbot::get_instance();