options = get_option('mxchat_options'); $this->chat_count = get_option('mxchat_chat_count', 0); $this->is_activated = $this->is_license_active(); // Initialize default options if they are not set if (!$this->options) { $this->initialize_default_options(); } // Add admin menu and initialize settings add_action('admin_menu', array($this, 'mxchat_add_plugin_page')); add_action('admin_init', array($this, 'mxchat_page_init')); add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets')); add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history')); add_action('admin_post_mxchat_submit_content', array($this, 'mxchat_handle_content_submission')); add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt')); add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history')); add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history')); add_action('admin_post_mxchat_submit_sitemap', array($this, 'mxchat_handle_sitemap_submission')); add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body')); add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets')); add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets')); add_action('wp_ajax_mxchat_activate_license', array($this, 'mxchat_handle_activate_license')); add_action('admin_notices', array($this, 'mxchat_display_admin_notice')); } // Method to check if the license is active private function is_license_active() { $license_status = get_option('mxchat_license_status', 'inactive'); return $license_status === 'active'; } // Initialize default options private function initialize_default_options() { $default_options = array( 'api_key' => '', 'system_prompt_instructions' => '[EXAMPLE INSTRUCTIONS] You are an AI Chatbot assistant for this website. The primary subject you should focus on is [insert proper subject here]. Your main goal is to assist visitors with questions related to this specific topic. Here are some key things to keep in mind: - Your name is [Chatbot Name]. Always introduce yourself as this name when appropriate. - Stay focused on topics related to [insert proper subject here]. If a visitor asks about an unrelated topic, politely redirect the conversation to how you can assist them with this subject. If there is an exception topic (e.g., "parking") that you should assist with, you may do so if instructed. - When appropriate, highlight the benefits of [insert proper subject here]. Offer to guide visitors to relevant pages or provide them with more information. - If a visitor asks for a purchase link or further information, provide them with this link: [Insert Purchase Link Here]. Always ensure that the link is relevant and directly related to the website\'s offerings. - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction. - If you reference specific content, provide a hyperlink to the relevant page using hypertext. Avoid including links that do not directly relate to the content or answer the visitor\'s query. - Provide answers based on the knowledge available to you. If you do not have an answer to a specific question, let the visitor know that you don’t have the information and suggest where they might find it or offer to help with something else.', 'model' => 'gpt-3.5-turbo', 'rate_limit' => '100', 'rate_limit_message' => 'Rate limit exceeded. Please try again later.', 'top_bar_title' => 'MxChat', 'intro_message' => 'Hello! How can I assist you today?', 'input_copy' => 'How can I assist?', 'append_to_body' => 'off', 'close_button_color' => '#fff', 'chatbot_bg_color' => '#fff', 'user_message_bg_color' => '#fff', 'user_message_font_color' => '#212121', 'bot_message_bg_color' => '#212121', 'bot_message_font_color' => '#fff', 'top_bar_bg_color' => '#212121', 'send_button_font_color' => '#212121', 'chat_input_font_color' => '#212121', 'chatbot_background_color' => '#212121', 'icon_color' => '#fff', 'enable_woocommerce_integration' => '0', 'enable_woocommerce_order_access' => '0', 'link_target_toggle' => 'off', 'pre_chat_message' => 'Hey there! Ask me anything!', // New fields for Loops Integration 'loops_api_key' => '', 'loops_mailing_list' => '', 'trigger_keywords' => '', 'triggered_phrase_response' => 'Would you like to join our mailing list? Please provide your email below.', 'email_capture_response' => 'Thank you for providing your email! You\'ve been added to our list.', ); // Merge existing options with defaults $existing_options = get_option('mxchat_options', array()); $merged_options = wp_parse_args($existing_options, $default_options); // Update the options if they have changed if ($existing_options !== $merged_options) { update_option('mxchat_options', $merged_options); } // Update the $this->options property $this->options = $merged_options; } public function mxchat_add_plugin_page() { // Main menu page add_menu_page( 'MxChat Settings', 'MxChat', 'manage_options', 'mxchat-max', array($this, 'mxchat_create_admin_page'), 'dashicons-testimonial', 6 ); // Submenu page for Knowledge add_submenu_page( 'mxchat-max', 'Prompts', 'Knowledge', 'manage_options', 'mxchat-prompts', array($this, 'mxchat_create_prompts_page') ); // Submenu page for Chat Transcripts add_submenu_page( 'mxchat-max', // Corrected parent slug to match the main menu 'Chat Transcripts', 'Transcripts', 'manage_options', 'mxchat-transcripts', array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_ ); // Submenu page for Activation Key add_submenu_page( 'mxchat-max', 'Pro Upgrade', 'Pro Upgrade', 'manage_options', 'mxchat-activation', array($this, 'mxchat_create_activation_page') ); } public function mxchat_handle_content_submission() { // Check if the form was submitted and the user has sufficient permissions if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) { return; } // Verify the nonce field for security $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : ''; if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) { wp_die('Nonce verification failed.'); } // Sanitize the content input $article_content = sanitize_textarea_field($_POST['article_content']); // Sanitize the URL input (optional URL) $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : ''; // Default to empty if not provided // Generate the embedding vector for the content $embedding_vector = $this->mxchat_generate_embedding($article_content); global $wpdb; $table_name = $wpdb->prefix . 'mxchat_system_prompt_content'; // Check if the 'source_url' column exists in the table and add it if it doesn't if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') { // Use wpdb::query and a prepared statement to avoid SQL injection $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''")); } if (is_array($embedding_vector)) { // Serialize the embedding vector before storing it $embedding_vector_serialized = serialize($embedding_vector); // Insert the content, embedding vector, and source URL into the database, using a prepared statement $inserted = $wpdb->insert( $table_name, array( 'article_content' => $article_content, 'embedding_vector' => $embedding_vector_serialized, 'source_url' => $article_url, // Insert the URL, empty string if not provided ), array( '%s', // Format for article_content (string) '%s', // Format for embedding_vector (serialized string) '%s', // Format for source_url (string) ) ); if ($inserted === false) { error_log('Error inserting content: ' . $wpdb->last_error); set_transient('mxchat_admin_notice_error', 'Error inserting content into the database. Please try again.', 30); } else { set_transient('mxchat_admin_notice_success', 'Content successfully submitted!', 30); } } else { error_log('Embedding generation failed for article content: ' . $article_content); set_transient('mxchat_admin_notice_error', 'Embedding generation failed. Please ensure your API key is correct and try again.', 30); } // Redirect after setting the transient wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } public function mxchat_display_admin_notice() { // Success notice if ($message = get_transient('mxchat_admin_notice_success')) { ?>
| ID | Article Content | URL | Actions |
|---|---|---|---|
| id); ?> | article_content))); ?> | source_url)): ?> source_url); ?> N/A | Delete |
| No prompts found. | |||
Enter your Loops API Key here. (See FAQ for details)
'; } public function mxchat_loops_mailing_list_callback() { // Retrieve Loops API key and lists $loops_api_key = isset($this->options['loops_api_key']) ? $this->options['loops_api_key'] : ''; $selected_list = isset($this->options['loops_mailing_list']) ? $this->options['loops_mailing_list'] : ''; if ($loops_api_key) { // Fetch lists from Loops API $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key); if ($lists) { echo ''; } else { echo 'No lists found. Please verify your API Key.
'; } } else { echo 'Enter a valid Loops API Key to load mailing lists.
'; } } public function mxchat_trigger_keywords_callback() { $trigger_keywords = isset($this->options['trigger_keywords']) ? $this->options['trigger_keywords'] : ''; echo ''; echo 'Enter comma-separated trigger keywords (e.g., subscribe, discount, newsletter).
'; } public function mxchat_triggered_phrase_response_callback() { $triggered_response = isset($this->options['triggered_phrase_response']) ? $this->options['triggered_phrase_response'] : ''; echo ''; echo 'Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.
'; } public function mxchat_email_capture_response_callback() { $email_capture_response = isset($this->options['email_capture_response']) ? $this->options['email_capture_response'] : 'Thank you for providing your email! You\'ve been added to our list.'; echo ''; echo 'Enter the message to send when a user provides their email.
'; } public function mxchat_pre_chat_message_callback() { printf( '', isset($this->options['pre_chat_message']) ? esc_textarea($this->options['pre_chat_message']) : '' ); } // Callback for AI Instructions textarea public function system_prompt_instructions_callback() { printf( '', isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : '' ); } public function mxchat_model_callback() { $models = array( 'gpt-4o' => 'gpt-4o', 'gpt-4o-mini' => 'gpt-4o-mini', 'gpt-4-turbo' => 'gpt-4-turbo', 'gpt-4' => 'gpt-4', 'gpt-3.5-turbo' => 'gpt-3.5-turbo', ); $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo'; echo ''; } public function mxchat_top_bar_title_callback() { printf( '', isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '' ); } public function mxchat_intro_message_callback() { printf( '', isset($this->options['intro_message']) ? esc_textarea($this->options['intro_message']) : 'Hello! How can I assist you today?' ); } public function mxchat_input_copy_callback() { printf( '', isset($this->options['input_copy']) ? esc_attr($this->options['input_copy']) : 'How can I assist?' ); echo 'This is the placeholder text for the chat input field.
'; } public function mxchat_close_button_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; echo 'Enable this option to automatically append the chat widget to the body of every page (recommended). No need to manually use the shortcode [mxchat_chatbot floating="yes"].
'; } public function mxchat_privacy_toggle_callback() { // Check if the privacy toggle is enabled $privacy_toggle_checked = isset($this->options['privacy_toggle']) && $this->options['privacy_toggle'] === 'on' ? 'checked' : ''; // Retrieve the stored privacy text if it exists $privacy_text = isset($this->options['privacy_text']) ? wp_kses_post($this->options['privacy_text']) : 'By chatting, you agree to our privacy policy.'; // Output the toggle switch echo ''; echo 'Enable this option to display a privacy notice below the chat widget.
'; // Output the custom text input field printf( '', esc_textarea($privacy_text) ); echo 'Enter the privacy policy text. You can include HTML links.
'; } public function mxchat_complianz_toggle_callback() { // Check if the Complianz toggle is enabled $complianz_toggle_checked = isset($this->options['complianz_toggle']) && $this->options['complianz_toggle'] === 'on' ? 'checked' : ''; // Check if the plugin is activated (paid feature) $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo 'Enable this option to apply Complianz consent logic to the chatbot.
'; // If the feature is not activated, show the Pro feature overlay if (!$this->is_activated) { echo ''; } echo 'Enable to open links in a new tab (default is to open in the same tab).
'; } public function mxchat_enqueue_admin_assets() { wp_enqueue_style('wp-color-picker'); // Get the plugin version or file modification time for cache busting $plugin_version = '1.1.4'; // Replace this with your plugin's version // File paths $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js'; $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js'; $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css'; $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js'; // Check if files exist and get modification times $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version; $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version; $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version; $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version; // Enqueue scripts and styles with corrected paths wp_enqueue_script( 'mxchat-color-picker', plugin_dir_url(__FILE__) . '../js/my-color-picker.js', array('wp-color-picker'), $color_picker_version, true ); wp_enqueue_script( 'mxchat-embedding-check', plugin_dir_url(__FILE__) . '../js/embedding-check.js', array(), $embedding_check_version, true ); wp_enqueue_script( 'mxchat-transcripts-js', plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js', array('jquery'), $transcripts_js_version, true ); wp_enqueue_script( 'mxchat-admin-js', plugin_dir_url(__FILE__) . '../js/mxchat-admin.js', array('jquery'), $plugin_version, true ); wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('mxchat_activate_license_nonce'), )); wp_enqueue_style( 'mxchat-admin-css', plugin_dir_url(__FILE__) . '../css/admin-style.css', array(), $admin_css_version ); wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array( 'ajax_url' => admin_url('admin-ajax.php'), 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off', 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff', 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121', 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121', 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff', 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121', 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121', 'close_button_color' => $this->options['close_button_color'] ?? '#fff', 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121', 'icon_color' => $this->options['icon_color'] ?? '#fff', 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121', 'pre_chat_message' => $this->options['pre_chat_message'] ?? 'Hey there! Ask me anything!', 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.', // New fields for Loops Integration 'loops_api_key' => $this->options['loops_api_key'] ?? '', 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '', 'trigger_keywords' => $this->options['trigger_keywords'] ?? '', 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? 'Would you like to join our mailing list? Please provide your email below.', 'email_capture_response' => $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.' )); } public function mxchat_sanitize($input) { $new_input = array(); if (isset($input['api_key'])) { $new_input['api_key'] = sanitize_text_field($input['api_key']); } if (isset($input['enable_woocommerce_integration'])) { $new_input['enable_woocommerce_integration'] = isset($input['enable_woocommerce_integration']) && $input['enable_woocommerce_integration'] === '1' ? '1' : '0'; } if (isset($input['privacy_toggle'])) { $new_input['privacy_toggle'] = $input['privacy_toggle']; } if (isset($input['complianz_toggle'])) { $new_input['complianz_toggle'] = $input['complianz_toggle']; } // Handle custom privacy text input if (isset($input['privacy_text'])) { // Allow basic HTML for links $new_input['privacy_text'] = wp_kses_post($input['privacy_text']); } if (isset($input['enable_woocommerce_order_access'])) { $new_input['enable_woocommerce_order_access'] = isset($input['enable_woocommerce_order_access']) && $input['enable_woocommerce_order_access'] === '1' ? '1' : '0'; } if (isset($input['system_prompt_instructions'])) { $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']); } if (isset($input['mxchat_pro_email'])) { $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']); } if (isset($input['mxchat_activation_key'])) { $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']); } if (isset($input['append_to_body'])) { $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off'; } if (isset($input['top_bar_title'])) { $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']); } if (isset($input['intro_message'])) { $new_input['intro_message'] = sanitize_text_field($input['intro_message']); } if (isset($input['input_copy'])) { $new_input['input_copy'] = sanitize_text_field($input['input_copy']); } if (isset($input['rate_limit_message'])) { $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']); } if (isset($input['rate_limit'])) { $allowed_limits = array('5', '10', '15', '20', '100'); $rate_limit = sanitize_text_field($input['rate_limit']); if (in_array($rate_limit, $allowed_limits, true)) { $new_input['rate_limit'] = $rate_limit; } else { // Set a default or a fallback value in case of an invalid entry $new_input['rate_limit'] = '100'; } } if (isset($input['pre_chat_message'])) { $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']); } if (isset($input['model'])) { $allowed_models = array( 'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo', ); if (in_array($input['model'], $allowed_models)) { $new_input['model'] = sanitize_text_field($input['model']); } } // Sanitize new pro features if (isset($input['close_button_color'])) { $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']); } if (isset($input['chatbot_bg_color'])) { $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']); } if (isset($input['woocommerce_consumer_key'])) { $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']); } if (isset($input['woocommerce_consumer_secret'])) { $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']); } if (isset($input['user_message_bg_color'])) { $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']); } if (isset($input['user_message_font_color'])) { $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']); } if (isset($input['bot_message_bg_color'])) { $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']); } if (isset($input['bot_message_font_color'])) { $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']); } if (isset($input['top_bar_bg_color'])) { $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']); } if (isset($input['send_button_font_color'])) { $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']); } if (isset($input['chatbot_background_color'])) { $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']); } if (isset($input['icon_color'])) { $new_input['icon_color'] = sanitize_hex_color($input['icon_color']); } if (isset($input['chat_input_font_color'])) { $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']); } // Sanitize link_target_toggle if (isset($input['link_target_toggle'])) { $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off'; } // Sanitize Loops API Key if (isset($input['loops_api_key'])) { $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']); } // Sanitize Loops Mailing List if (isset($input['loops_mailing_list'])) { $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']); } // Sanitize Trigger Keywords if (isset($input['trigger_keywords'])) { $new_input['trigger_keywords'] = sanitize_text_field($input['trigger_keywords']); } // Sanitize Triggered Phrase Response if (isset($input['triggered_phrase_response'])) { $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']); } if (isset($input['email_capture_response'])) { $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']); } return $new_input; } // Method to append the chatbot to the body public function mxchat_append_chatbot_to_body() { $options = get_option('mxchat_options'); if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') { echo do_shortcode('[mxchat_chatbot floating="yes"]'); } } private function mxchat_extract_main_content($html) { $dom = new DOMDocument; libxml_use_internal_errors(true); // Suppress HTML parsing errors @$dom->loadHTML($html); libxml_clear_errors(); $xpath = new DOMXPath($dom); // Simplified selectors focusing on common content areas $selectors = [ '//article', '//*[@id="content"]', '//*[@class="entry-content"]', '//main', ]; foreach ($selectors as $selector) { $nodes = $xpath->query($selector); if ($nodes->length > 0) { $content = ''; foreach ($nodes as $node) { $content .= $dom->saveHTML($node); } return $content; } } // Fallback: Return the entire body content if no specific selector matches $body = $dom->getElementsByTagName('body'); return $body->length > 0 ? $dom->saveHTML($body->item(0)) : $html; } public function mxchat_handle_sitemap_submission() { // Check if the form was submitted and the user has sufficient permissions if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) { return; } // Verify the nonce field for security check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); // Sanitize and retrieve the submitted URL $submitted_url = esc_url_raw($_POST['sitemap_url']); // Accept either a sitemap URL or a regular URL // Fetch the content of the submitted URL $response = wp_remote_get($submitted_url); if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) { set_transient('mxchat_admin_notice_error', 'Failed to fetch the URL. Please check the URL and try again.', 30); wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } $content_type = wp_remote_retrieve_header($response, 'content-type'); $body_content = wp_remote_retrieve_body($response); // Check if the URL points to a sitemap (XML) or a regular HTML page if (strpos($content_type, 'xml') !== false || strpos($body_content, '