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'));
// Add the AJAX handler for logged-in users
add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt'));
add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
// Define admin_post actions for form submission handling
add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent'));
add_action('admin_post_mxchat_update_intent_threshold', array($this, 'mxchat_handle_update_intent_threshold'));
}
// 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' => '',
'xai_api_key' => '',
'claude_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].
- 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.
- Do not make up links to pages. Only use specific links you see in your knowledgebase.
- Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
- 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.',
'model' => 'gpt-4o',
'rate_limit_logged_in' => '100',
'rate_limit_logged_out' => '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',
'link_target_toggle' => 'off',
'pre_chat_message' => 'Hey there! Ask me anything!',
// New fields for Loops Integration
'loops_api_key' => '',
'loops_mailing_list' => '',
'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.',
'popular_question_1' => '',
'popular_question_2' => '',
'popular_question_3' => '',
'pdf_intent_trigger_text' => "Please provide the URL to the PDF you'd like to discuss.",
'pdf_intent_success_text' => "I've processed the PDF. What questions do you have about it?",
'pdf_intent_error_text' => "Sorry, I couldn't process the PDF. Please ensure it's a valid file.",
'pdf_max_pages' => 69,
);
// 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 Intents
add_submenu_page(
'mxchat-max', // Parent slug
'MxChat Intents', // Page title
'Intents', // Menu title
'manage_options', // Capability
'mxchat-intents', // Menu slug
array($this, 'mxchat_intents_page_html') // Callback function
);
// 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')) {
?>
is_activated): ?>
Limited-time offer: Get a lifetime MxChat Pro license for just $49.97 until 01.01.25! After that, it switches to an annual license at $99.97/year. Purchase now and be grandfathered into the lifetime deal!
Upgrade to MxChat Pro today!
';
}
// Format the timestamp for display
$formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
// Determine the role to display (user identifier or email for users, bot for the AI)
$role = $transcript->role;
if ($role === 'user') {
// Sanitize email if available
if (!empty($transcript->user_email)) {
$role = sanitize_email($transcript->user_email);
} else {
// Sanitize user identifier, anonymize if it's an IP address
$user_identifier = sanitize_text_field($transcript->user_identifier);
// Check if the user identifier is an IP address and anonymize it
if (filter_var($user_identifier, FILTER_VALIDATE_IP)) {
$role = preg_replace('/\.\d+$/', '.xxx', $user_identifier); // Mask the last octet
} else {
$role = $user_identifier; // If it's not an IP, just display the identifier
}
}
}
// Output the chat message
echo '
';
}
private function mxchat_add_option_field($id, $title, $callback = '') {
add_settings_field(
$id,
$title,
$callback ? array($this, $callback) : array($this, $id . '_callback'),
'mxchat-max',
'mxchat_setting_section_id',
$id === 'model' ? ['label_for' => 'model'] : []
);
}
public function api_key_callback() {
$apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
echo '';
echo '';
}
public function xai_api_key_callback() {
// Check if the feature is activated (paid feature)
$disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
$class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
// Render the input field for the X.AI API key
echo '
';
printf(
'',
isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '',
$disabled
);
echo '';
// If the feature is not activated, show the overlay with a "Pro Only" message
if (!$this->is_activated) {
echo '
';
echo '';
echo '
';
}
echo '
';
}
public function claude_api_key_callback() {
// Check if the feature is activated (paid feature)
$disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
$class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
// Render the input field for the Claude API key
echo '
';
printf(
'',
isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '',
$disabled
);
echo '';
// If the feature is not activated, show the overlay with a "Pro Only" message
if (!$this->is_activated) {
echo '
';
}
public function mxchat_loops_api_key_callback() {
$loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
echo '
';
printf(
'',
$loops_api_key
);
echo '';
echo '
';
echo '
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_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(
'X.AI Models' => array(
'grok-beta' => 'grok-beta (Early Beta)'
),
'Claude Models' => array(
'claude-3-5-sonnet-20241022' => 'Claude 3.5 Sonnet (Most Intelligent)',
'claude-3-opus-20240229' => 'Claude 3 Opus (Highly Complex Tasks)',
'claude-3-sonnet-20240229' => 'Claude 3 Sonnet (Balanced)',
'claude-3-haiku-20240307' => 'Claude 3 Haiku (Fastest)'
),
'OpenAI Models' => array(
'gpt-4o' => 'GPT-4o (Recommended)',
'gpt-4o-mini' => 'GPT-4o Mini (Fast and Lightweight)',
'gpt-4-turbo' => 'GPT-4 Turbo (High-Performance)',
'gpt-4' => 'GPT-4 (High Intelligence)',
'gpt-3.5-turbo' => 'GPT-3.5 Turbo (Affordable and Fast)'
)
);
$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 '
';
// Output the toggle switch
echo '';
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 '';
echo '
';
}
echo '
';
}
public function mxchat_link_target_toggle_callback() {
// Check if the toggle is enabled in the options
$link_target_toggle = isset($this->options['link_target_toggle']) && $this->options['link_target_toggle'] === 'on' ? 'checked' : '';
// Output the toggle switch
echo '';
echo '
Enable to open links in a new tab (default is to open in the same tab).
';
}
public function mxchat_chat_persistence_toggle_callback() {
// Check if chat persistence toggle is enabled
$chat_persistence_toggle_checked = isset($this->options['chat_persistence_toggle']) && $this->options['chat_persistence_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 '
';
// Output the toggle switch
echo '';
echo '
Enable to keep chat history when users navigate tabs or return to the site within 24 hours.
';
// If not activated, show Pro feature overlay
if (!$this->is_activated) {
echo '
';
echo '';
echo '
';
}
echo '
';
}
public function mxchat_popular_question_1_callback() {
printf(
'',
isset($this->options['popular_question_1']) ? esc_attr($this->options['popular_question_1']) : ''
);
echo '
This will be the first popular question in the chatbot.
';
}
public function mxchat_popular_question_2_callback() {
// 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 '