PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.3
MxChat – AI Chatbot & Content Generation for WordPress v1.3
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / includes / class-mxchat-admin.php

class-mxchat-admin.php in MxChat – AI Chatbot & Content Generation for WordPress 1.3, at includes/class-mxchat-admin.php

2,989 lines 120.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit; // Exit if accessed directly
4 }
5
6 class MxChat_Admin {
7 private $options;
8 private $chat_count;
9 private $is_activated;
10
11 public function __construct() {
12 $this->options = get_option('mxchat_options');
13 $this->chat_count = get_option('mxchat_chat_count', 0);
14 $this->is_activated = $this->is_license_active();
15
16 // Initialize default options if they are not set
17 if (!$this->options) {
18 $this->initialize_default_options();
19 }
20
21 // Add admin menu and initialize settings
22 add_action('admin_menu', array($this, 'mxchat_add_plugin_page'));
23 add_action('admin_init', array($this, 'mxchat_page_init'));
24 add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets'));
25 add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history'));
26 add_action('admin_post_mxchat_submit_content', array($this, 'mxchat_handle_content_submission'));
27 add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
28 add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
29 add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
30 add_action('admin_post_mxchat_submit_sitemap', array($this, 'mxchat_handle_sitemap_submission'));
31 add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
32 add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
33 add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets'));
34 add_action('wp_ajax_mxchat_activate_license', array($this, 'mxchat_handle_activate_license'));
35 add_action('admin_notices', array($this, 'mxchat_display_admin_notice'));
36 // Add the AJAX handler for logged-in users
37 add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt'));
38
39
40 }
41
42 // Method to check if the license is active
43 private function is_license_active() {
44 $license_status = get_option('mxchat_license_status', 'inactive');
45 return $license_status === 'active';
46 }
47
48 // Initialize default options
49 private function initialize_default_options() {
50 $default_options = array(
51 'api_key' => '',
52 'xai_api_key' => '',
53 'claude_api_key' => '',
54 '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:
55 - Your name is [Chatbot Name]. Always introduce yourself as this name when appropriate.
56 - 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.
57 - When appropriate, highlight the benefits of [insert proper subject here]. Offer to guide visitors to relevant pages or provide them with more information.
58 - 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.
59 - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
60 - 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.
61 - 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.',
62 'model' => 'gpt-3.5-turbo',
63 'rate_limit' => '100',
64 'rate_limit_message' => 'Rate limit exceeded. Please try again later.',
65 'top_bar_title' => 'MxChat',
66 'intro_message' => 'Hello! How can I assist you today?',
67 'input_copy' => 'How can I assist?',
68 'append_to_body' => 'off',
69 'close_button_color' => '#fff',
70 'chatbot_bg_color' => '#fff',
71 'user_message_bg_color' => '#fff',
72 'user_message_font_color' => '#212121',
73 'bot_message_bg_color' => '#212121',
74 'bot_message_font_color' => '#fff',
75 'top_bar_bg_color' => '#212121',
76 'send_button_font_color' => '#212121',
77 'chat_input_font_color' => '#212121',
78 'chatbot_background_color' => '#212121',
79 'icon_color' => '#fff',
80 'enable_woocommerce_integration' => '0',
81 'link_target_toggle' => 'off',
82 'pre_chat_message' => 'Hey there! Ask me anything!',
83
84 // New fields for Loops Integration
85 'loops_api_key' => '',
86 'loops_mailing_list' => '',
87 'triggered_phrase_response' => 'Would you like to join our mailing list? Please provide your email below.',
88 'email_capture_response' => 'Thank you for providing your email! You\'ve been added to our list.',
89 'popular_question_1' => '',
90 'popular_question_2' => '',
91 'popular_question_3' => '',
92 );
93
94
95 // Merge existing options with defaults
96 $existing_options = get_option('mxchat_options', array());
97 $merged_options = wp_parse_args($existing_options, $default_options);
98
99 // Update the options if they have changed
100 if ($existing_options !== $merged_options) {
101 update_option('mxchat_options', $merged_options);
102 }
103
104 // Update the $this->options property
105 $this->options = $merged_options;
106 }
107
108
109
110 public function mxchat_add_plugin_page() {
111 // Main menu page
112 add_menu_page(
113 'MxChat Settings',
114 'MxChat',
115 'manage_options',
116 'mxchat-max',
117 array($this, 'mxchat_create_admin_page'),
118 'dashicons-testimonial',
119 6
120 );
121
122 // Submenu page for Knowledge
123 add_submenu_page(
124 'mxchat-max',
125 'Prompts',
126 'Knowledge',
127 'manage_options',
128 'mxchat-prompts',
129 array($this, 'mxchat_create_prompts_page')
130 );
131
132 // Submenu page for Chat Transcripts
133 add_submenu_page(
134 'mxchat-max', // Corrected parent slug to match the main menu
135 'Chat Transcripts',
136 'Transcripts',
137 'manage_options',
138 'mxchat-transcripts',
139 array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
140 );
141
142 // Submenu page for Intents
143 add_submenu_page(
144 'mxchat-max', // Parent slug
145 'MxChat Intents', // Page title
146 'Intents', // Menu title
147 'manage_options', // Capability
148 'mxchat-intents', // Menu slug
149 array($this, 'mxchat_intents_page_html') // Callback function
150 );
151
152 // Submenu page for Activation Key
153 add_submenu_page(
154 'mxchat-max',
155 'Pro Upgrade',
156 'Pro Upgrade',
157 'manage_options',
158 'mxchat-activation',
159 array($this, 'mxchat_create_activation_page')
160 );
161
162
163 }
164
165
166 public function mxchat_handle_content_submission() {
167 // Check if the form was submitted and the user has sufficient permissions
168 if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
169 return;
170 }
171
172 // Verify the nonce field for security
173 $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
174 if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
175 wp_die('Nonce verification failed.');
176 }
177
178 // Sanitize the content input
179 $article_content = sanitize_textarea_field($_POST['article_content']);
180
181 // Sanitize the URL input (optional URL)
182 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : ''; // Default to empty if not provided
183
184 // Generate the embedding vector for the content
185 $embedding_vector = $this->mxchat_generate_embedding($article_content);
186
187 global $wpdb;
188 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
189
190 // Check if the 'source_url' column exists in the table and add it if it doesn't
191 if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
192 // Use wpdb::query and a prepared statement to avoid SQL injection
193 $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
194 }
195
196 if (is_array($embedding_vector)) {
197 // Serialize the embedding vector before storing it
198 $embedding_vector_serialized = serialize($embedding_vector);
199
200 // Insert the content, embedding vector, and source URL into the database, using a prepared statement
201 $inserted = $wpdb->insert(
202 $table_name,
203 array(
204 'article_content' => $article_content,
205 'embedding_vector' => $embedding_vector_serialized,
206 'source_url' => $article_url, // Insert the URL, empty string if not provided
207 ),
208 array(
209 '%s', // Format for article_content (string)
210 '%s', // Format for embedding_vector (serialized string)
211 '%s', // Format for source_url (string)
212 )
213 );
214
215 if ($inserted === false) {
216 //error_log('Error inserting content: ' . $wpdb->last_error);
217 set_transient('mxchat_admin_notice_error', 'Error inserting content into the database. Please try again.', 30);
218 } else {
219 set_transient('mxchat_admin_notice_success', 'Content successfully submitted!', 30);
220 }
221
222 } else {
223 //error_log('Embedding generation failed for article content: ' . $article_content);
224 set_transient('mxchat_admin_notice_error', 'Embedding generation failed. Please ensure your API key is correct and try again.', 30);
225 }
226
227 // Redirect after setting the transient
228 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
229 exit;
230 }
231
232 public function mxchat_display_admin_notice() {
233 // Success notice
234 if ($message = get_transient('mxchat_admin_notice_success')) {
235 ?>
236 <div class="notice notice-success is-dismissible">
237 <p><?php echo esc_html($message); ?></p>
238 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
239 </div>
240 <?php
241 delete_transient('mxchat_admin_notice_success'); // Clear the transient after displaying
242 }
243
244 // Error notice
245 if ($message = get_transient('mxchat_admin_notice_error')) {
246 ?>
247 <div class="notice notice-error is-dismissible">
248 <p><?php echo esc_html($message); ?></p>
249 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
250 </div>
251 <?php
252 delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
253 }
254 }
255
256
257 public function mxchat_handle_delete_prompt() {
258 // Sanitize and validate nonce using wp_unslash and sanitize_text_field
259 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
260 if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
261 wp_die('Nonce verification failed.');
262 }
263
264 // Check user permissions
265 if (!current_user_can('manage_options')) {
266 wp_die('You do not have sufficient permissions to delete prompts.');
267 }
268
269 // Sanitize and validate the 'id' parameter
270 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
271 if ($id <= 0) {
272 wp_die('Invalid prompt ID.');
273 }
274
275 global $wpdb;
276 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
277
278 // Clear relevant cache before deletion
279 $cache_key = 'prompt_' . $id;
280 wp_cache_delete($cache_key, 'mxchat_prompts');
281
282 // Delete the record using a prepared statement
283 $deleted = $wpdb->delete($table_name, array('id' => $id), array('%d'));
284
285 if ($deleted !== false) {
286 // Optionally, clear a general cache if you have one
287 wp_cache_delete('all_prompts', 'mxchat_prompts');
288 }
289
290 // Redirect to the prompts page with a success message
291 $redirect_url = add_query_arg(array(
292 'page' => 'mxchat-prompts',
293 'deleted' => 'true'
294 ), admin_url('admin.php'));
295
296 wp_safe_redirect($redirect_url);
297 exit;
298 }
299
300
301 public function mxchat_create_admin_page() {
302 ?>
303 <div class="wrap mxchat-admin">
304 <?php if (!$this->is_activated): ?>
305 <div class="mxchat-pro-banner">
306 <p>
307 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!
308 <a href="https://mxchat.ai/" target="_blank">Upgrade to MxChat Pro today!</a>
309 </p>
310 </div>
311 <?php endif; ?>
312 <h2 class="admin-title">Mx<span class="admin-emphasis">Chat</span></h2>
313 <h2 class="mxchat-nav-tab-wrapper">
314 <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot">Chatbot</a>
315 <a href="#embed" class="mxchat-nav-tab" data-tab="embed">Integrations</a>
316 <a href="#theme" class="mxchat-nav-tab" data-tab="theme">Theme</a>
317 <a href="#general" class="mxchat-nav-tab" data-tab="general">FAQ</a>
318 </h2>
319 <form method="post" action="options.php">
320 <?php settings_fields('mxchat_option_group'); ?>
321 <div id="chatbot" class="mxchat-tab-content active">
322 <?php do_settings_sections('mxchat-chatbot'); ?>
323 </div>
324
325
326
327 <div id="embed" class="mxchat-tab-content">
328 <div class="mxchat-settings-section">
329 <h2>WooCommerce Settings</h2>
330 <table class="form-table">
331 <?php do_settings_fields('mxchat-embed', 'mxchat_woocommerce_section'); ?>
332 </table>
333 </div>
334
335 <div class="section-divider"></div> <!-- Divider -->
336
337 <div class="mxchat-settings-section">
338 <h2>Loops Settings</h2>
339 <table class="form-table">
340 <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
341 </table>
342 </div>
343
344
345 <div class="section-divider"></div> <!-- Divider -->
346
347 <!-- Brave Search Settings Section -->
348 <div class="mxchat-settings-section">
349 <h2>Brave Search Settings</h2>
350 <table class="form-table">
351 <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
352 </table>
353 </div>
354 </div>
355
356
357
358
359
360 <div id="theme" class="mxchat-tab-content">
361 <?php do_settings_sections('mxchat-theme'); ?>
362 </div>
363 <div id="general" class="mxchat-tab-content">
364 <?php do_settings_sections('mxchat-general'); ?>
365 <p>If you’re having trouble with setup or getting the responses you need, we encourage you to review our <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
366
367 <div class="faq-item">
368 <h3>How does the Claude API integration work?</h3>
369 <p>
370 The Claude API, provided by Anthropic, allows for intelligent, context-aware chatbot responses in MxChat. To use it, you will need both an OpenAI API key and a Claude API key. This is necessary because the system utilizes OpenAI for vector embedding in the Retrieval-Augmented Generation (RAG) process, as Claude does not currently offer an embedding API.
371 </p>
372 <p>
373 When using the Claude API, your custom content is sent to Claude for generating responses, while the embeddings are processed through OpenAI. This ensures your chatbot provides accurate and engaging responses while maintaining knowledge relevant to your website.
374 </p>
375 <p>
376 You can obtain your Claude API key by signing up on the <a href="https://www.anthropic.com/api" target="_blank" rel="noopener">Anthropic API page</a>.
377 </p>
378 </div>
379
380 <div class="faq-item">
381 <h3>How does the X.AI API integration work?</h3>
382 <p>
383 The X.AI API, released on 10.21.24, is currently in beta. To use it, you will need both an OpenAI API key and an X.AI API key. This is because OpenAI handles vector embeddings in the Retrieval-Augmented Generation (RAG) process, as X.AI does not yet provide an embedding API.
384 </p>
385 <p>
386 Custom content is sent to the X.AI API for generating responses, while OpenAI processes the embeddings. This allows the chatbot to provide advanced responses while maintaining context from your website. You can get your X.AI API key from the <a href="https://docs.x.ai/docs" target="_blank" rel="noopener">X.AI API documentation</a>.
387 </p>
388 </div>
389
390 <div class="faq-item">
391 <h3>Do I need an OpenAI API key to use the chatbot?</h3>
392 <p>
393 Yes, you will need an OpenAI API key to power the chatbot. You can obtain an API key by signing up on the <a href="https://platform.openai.com/signup" target="_blank" rel="noopener">OpenAI platform</a>. After signing up, you must add credits to your account—typically, $5 in credits is sufficient to get started.
394 </p>
395 <p>
396 Once you have your API key, simply enter it in the chatbot's settings to enable functionality. The chatbot relies on OpenAI’s models for generating responses, so having sufficient credits in your OpenAI account is essential for smooth operation.
397 </p>
398 </div>
399
400 <div class="faq-item">
401 <h3>How do I add the chatbot to my site?</h3>
402 <p>
403 You can add the chatbot using the shortcode <code>[mxchat_chatbot floating="yes"]</code> or <code>[mxchat_chatbot floating="no"]</code>. For initial testing and styling, it's best to use the shortcode on a draft or non-public page. Once you’re ready to go live, enable the “Append Chat Widget to Body” option in the settings for site-wide integration (recommended) or add shortcode to the footer.
404 </p>
405 </div>
406
407 <div class="faq-item">
408 <h3>How does the chatbot use my content to generate responses?</h3>
409 <p>
410 The chatbot uses AI and vector embeddings to connect users with relevant information. When you submit content, it converts it into a mathematical format. When a user asks a question, the bot matches it to your stored content and generates a response based on relevance. For example, submitting "Our phone number is 910-123-4567" allows the bot to retrieve this information when asked about contact details. To ensure related information is provided together, submit it in one entry.
411 </p>
412 </div>
413
414 <div class="faq-item">
415 <h3>Why does the chatbot sometimes make up links or information?</h3>
416 <p>
417 Occasionally, the chatbot may generate inaccurate links or information, known as "hallucinations." To minimize this, you can add system instructions to guide its behavior. For example, include an instruction like: "Only provide links you directly have access to or retrieve from the knowledge base. Do not make up links or information that you do not have direct access to." This helps the bot stay aligned with your content.
418 </p>
419 </div>
420
421 <div class="faq-item">
422 <h3>How do intents work in MxChat?</h3>
423 <p>
424 Intents allow MxChat to recognize user requests and trigger specific actions, such as capturing emails or displaying product information. This helps provide a more interactive and responsive experience. For a detailed explanation of each intent, please refer to our <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">Intents Documentation</a>.
425 </p>
426 </div>
427
428
429 <div class="faq-item">
430 <h3>How does the Complianz integration work?</h3>
431 <p>
432 The Pro version offers direct integration with the Complianz GDPR plugin, making it easy to stay compliant with GDPR. If Complianz is enabled on your website, users must accept consent before the chatbot widget appears. The chatbot will only display once the user has accepted, ensuring compliance with data privacy regulations.
433 </p>
434 </div>
435
436 <div class="faq-item">
437 <h3>How does the WooCommerce integration work?</h3>
438 <p>
439 With WooCommerce integration, the chatbot automatically embeds new products and updates existing ones. For already-published products, you can click update or submit the product sitemap. The “Order History Access” setting allows the bot to access users' order history (requires login) and assist with order inquiries. To enable the “Add to Cart” feature, add this system instruction: “After discussing a product, ask if the user wants to add it to their cart. The user must say 'Add to cart' exactly. Currently, this feature supports English only, with more languages coming soon.
440 </p>
441 </div>
442
443 <div class="faq-item">
444 <h3>Why isn't my chatbot responding as expected?</h3>
445 <p>
446 AI chatbots can sometimes behave in unexpected ways, especially if you're new to configuring AI for specific tasks. We're committed to helping you get the most out of your chatbot experience. While we’re working on comprehensive guides and video tutorials, our team is here to assist you directly. If your chatbot isn't delivering the responses you need, please don’t hesitate to <a href="https://mxchat.ai/contact/" target="_blank" rel="noopener noreferrer">contact us</a> for personalized support.
447 </p>
448 <p>
449 We’re dedicated to your success and ready to guide you in aligning the AI's behavior to meet your goals.
450 </p>
451 </div>
452
453 <div class="faq-item">
454 <h3>What is Loops, and how do I get an API key?</h3>
455 <p>
456 Loops is a powerful SaaS email service that helps you automate and enhance your email marketing campaigns, making it easy to reach and engage with your audience. To integrate Loops with MxChat, you’ll need an API key from Loops. You can obtain this key by logging into your Loops account and navigating to the API settings. Visit the <a href="https://loops.so" target="_blank" rel="noopener noreferrer">Loops website</a> to get started or to sign up for an account.
457 </p>
458 </div>
459
460
461 </div>
462 <?php submit_button(); ?>
463 </form>
464 </div>
465 <?php
466 }
467
468
469 public function mxchat_create_transcripts_page() {
470 ?>
471 <div class="wrap mxchat-admin">
472 <h2><?php esc_html_e('Chat Transcripts', 'mxchat'); ?></h2>
473 <form id="mxchat-delete-form" method="post">
474 <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
475 <div class="mxchat-controls">
476 <label for="mxchat-select-all-transcripts" class="mxchat-select-all-label">
477 <input type="checkbox" id="mxchat-select-all-transcripts" /> <?php esc_html_e('Select All', 'mxchat'); ?>
478 </label>
479 <input type="submit" value="<?php esc_attr_e('Delete Selected', 'mxchat'); ?>" class="button button-primary delete-chats-button" />
480 </div>
481 <div id="mxchat-transcripts">
482 <!-- Transcripts will be loaded here -->
483 </div>
484 </form>
485 </div>
486 <?php
487 }
488
489
490
491
492 public function mxchat_create_prompts_page() {
493 global $wpdb;
494 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
495
496 // Verify the nonce before processing the request
497 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
498
499 if (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce')) {
500 // Sanitize input data
501 $search_query = isset($_GET['search']) ? sanitize_text_field($_GET['search']) : '';
502 $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
503 } else {
504 $search_query = '';
505 $current_page = 1;
506 }
507
508 $per_page = 10;
509
510 // Create cache keys
511 $cache_key_total = 'total_prompts_' . md5($search_query);
512 $cache_key_prompts = 'prompts_' . md5($search_query . '_' . $current_page);
513
514 // Retrieve total number of prompts from cache or database
515 $total_prompts = wp_cache_get($cache_key_total, 'mxchat_prompts');
516 if ($total_prompts === false) {
517 if (!empty($search_query)) {
518 $total_prompts = $wpdb->get_var(
519 $wpdb->prepare(
520 "SELECT COUNT(*) FROM {$table_name} WHERE article_content LIKE %s",
521 '%' . $wpdb->esc_like($search_query) . '%'
522 )
523 );
524 } else {
525 $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name}");
526 }
527 wp_cache_set($cache_key_total, $total_prompts, 'mxchat_prompts', 3600); // Cache for 1 hour
528 }
529
530 $total_pages = ceil($total_prompts / $per_page);
531 $offset = ($current_page - 1) * $per_page;
532
533 // Retrieve prompts from cache or database
534 $prompts = wp_cache_get($cache_key_prompts, 'mxchat_prompts');
535 if ($prompts === false) {
536 if (!empty($search_query)) {
537 $prompts = $wpdb->get_results(
538 $wpdb->prepare(
539 "SELECT * FROM {$table_name} WHERE article_content LIKE %s ORDER BY timestamp DESC LIMIT %d OFFSET %d",
540 '%' . $wpdb->esc_like($search_query) . '%',
541 $per_page,
542 $offset
543 )
544 );
545 } else {
546 $prompts = $wpdb->get_results(
547 $wpdb->prepare(
548 "SELECT * FROM {$table_name} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
549 $per_page,
550 $offset
551 )
552 );
553 }
554 wp_cache_set($cache_key_prompts, $prompts, 'mxchat_prompts', 3600); // Cache for 1 hour
555 }
556
557 ?>
558
559 <div class="wrap mxchat-admin">
560 <div class="mxchat-grid-container">
561
562 <!-- Submit Content -->
563 <div class="mxchat-grid-item full-width">
564 <h2>Submit Content</h2>
565 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
566 <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
567 <div class="mxchat-form-group">
568 <label for="article_content">Article Content:</label>
569 <textarea name="article_content" id="article_content" required></textarea>
570 </div>
571 <div class="mxchat-form-group">
572 <label for="article_url">Article URL (Optional):</label>
573 <input type="url" name="article_url" id="article_url" placeholder="Enter related URL for the content">
574 </div>
575 <input type="submit" name="submit_content" value="Submit Content" class="button button-primary submit-content-button" />
576 </form>
577 </div>
578
579
580 <!-- Submit Sitemap -->
581 <div class="mxchat-grid-item">
582 <h2>Submit Sitemap or Page URL</h2>
583 <form id="mxchat-sitemap-form" method="post" class="mxchat-sitemap-form" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
584 <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
585 <div class="mxchat-search-group">
586 <input type="url" name="sitemap_url" id="sitemap_url" placeholder="Sitemap or URL" required />
587 <input type="submit" name="submit_sitemap" value="Submit" class="button button-primary" />
588 </div>
589 </form>
590 <div id="mxchat-sitemap-loading" class="mxchat-spinner" style="display: none;"></div>
591 <div id="mxchat-loading-text" style="display: none; margin-top: 10px; color: #333;">Loading sitemap content into database, please wait...</div>
592 </div>
593
594 <!-- Search Knowledge -->
595 <div class="mxchat-grid-item">
596 <h2>Search Knowledge</h2>
597 <form method="get" id="knowledge-search">
598 <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
599 <input type="hidden" name="page" value="mxchat-prompts" />
600 <div class="mxchat-search-group">
601 <input type="text" name="search" placeholder="Search Knowledge" value="<?php echo esc_attr($search_query); ?>" />
602 <input type="submit" value="Search" class="button button-primary" />
603 </div>
604 </form>
605 </div>
606 </div>
607
608 <!-- Table below the forms -->
609 <div class="tablenav">
610 <div class="tablenav-pages">
611 <span class="displaying-num"><?php echo esc_html($total_prompts); ?> items</span>
612 <?php
613 $page_links = paginate_links(array(
614 'base' => add_query_arg('paged', '%#%', admin_url('admin.php?page=mxchat-prompts')),
615 'format' => '',
616 'prev_text' => __('&laquo; Previous'),
617 'next_text' => __('Next &raquo;'),
618 'total' => $total_pages,
619 'current' => $current_page,
620 'add_args' => array(
621 'search' => $search_query,
622 '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')
623 ),
624 ));
625
626 if ($page_links) {
627 echo '<div class="tablenav-pages">' . wp_kses_post($page_links) . '</div>';
628 }
629 ?>
630 </div>
631 </div>
632 <table class="mxchat-table">
633 <thead>
634 <tr>
635 <th>ID</th>
636 <th>Article Content</th>
637 <th>URL</th>
638 <th>Actions</th>
639 </tr>
640 </thead>
641 <tbody>
642 <?php if ($prompts) : ?>
643 <?php foreach ($prompts as $prompt) : ?>
644 <tr id="prompt-<?php echo esc_attr($prompt->id); ?>">
645 <td><?php echo esc_html($prompt->id); ?></td>
646 <td class="mxchat_article_content_dashboard">
647 <span class="content-view"><?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?></span>
648 <textarea class="content-edit" style="display:none;"><?php echo esc_textarea($prompt->article_content); ?></textarea>
649 </td>
650 <td class="mxchat_article_url_dashboard">
651 <span class="url-view">
652 <?php if (!empty($prompt->source_url)) : ?>
653 <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank"><?php echo esc_html($prompt->source_url); ?></a>
654 <?php else : ?>
655 N/A
656 <?php endif; ?>
657 </span>
658 <input type="url" class="url-edit" value="<?php echo esc_attr($prompt->source_url); ?>" style="display:none;" />
659 </td>
660 <td>
661 <button class="button edit-button" data-id="<?php echo esc_attr($prompt->id); ?>">Edit</button>
662 <button class="button save-button" data-id="<?php echo esc_attr($prompt->id); ?>" style="display:none;">Save</button>
663 <a href="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id) . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce'))); ?>" class="button delete-button">Delete</a>
664 </td>
665 </tr>
666 <?php endforeach; ?>
667 <?php else : ?>
668 <tr>
669 <td colspan="4">No prompts found.</td>
670 </tr>
671 <?php endif; ?>
672 </tbody>
673 </table>
674
675 </div>
676
677 <?php
678 }
679
680 public function mxchat_generate_embedding($text) {
681 $options = get_option('mxchat_options');
682 $api_key = $options['api_key'] ?? 'default_api_key';
683
684 $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
685 'body' => wp_json_encode(array(
686 'model' => 'text-embedding-ada-002',
687 'input' => $text
688 )),
689 'headers' => array(
690 'Authorization' => 'Bearer ' . $api_key,
691 'Content-Type' => 'application/json'
692 ),
693 ));
694
695 if (is_wp_error($response)) {
696 return null;
697 }
698
699 $response_data = json_decode(wp_remote_retrieve_body($response), true);
700 return $response_data['data'][0]['embedding'] ?? null;
701 }
702
703 public function mxchat_delete_chat_history() {
704 if (!current_user_can('manage_options')) {
705 echo wp_json_encode(['error' => 'You do not have sufficient permissions.']);
706 wp_die();
707 }
708
709 check_ajax_referer('mxchat_delete_chat_history', 'security');
710
711 global $wpdb;
712 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
713
714 if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
715 foreach ($_POST['delete_session_ids'] as $session_id) {
716 $session_id_sanitized = sanitize_text_field($session_id);
717
718 // Clear relevant cache before deletion
719 $cache_key = 'chat_session_' . $session_id_sanitized;
720 wp_cache_delete($cache_key, 'mxchat_chat_sessions');
721
722 // Perform the deletion
723 $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
724 }
725
726 // Optionally, clear a general cache if you have one
727 wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
728
729 echo wp_json_encode(['success' => 'Selected chat sessions have been deleted.']);
730 } else {
731 echo wp_json_encode(['error' => 'No chat sessions selected for deletion.']);
732 }
733
734 wp_die();
735 }
736
737
738 public function mxchat_save_inline_prompt() {
739 // Check for nonce security
740 check_ajax_referer('mxchat_save_inline_nonce');
741
742 // Verify permissions
743 if (!current_user_can('manage_options')) {
744 wp_send_json_error('Permission denied.');
745 return;
746 }
747
748 global $wpdb;
749 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
750
751 // Validate and sanitize input data
752 $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
753 $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : '';
754 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
755
756 if ($prompt_id > 0 && !empty($article_content)) {
757 // Re-generate the embedding vector for the updated content
758 $embedding_vector = $this->mxchat_generate_embedding($article_content);
759
760 if (is_array($embedding_vector)) {
761 // Serialize the embedding vector before storing it
762 $embedding_vector_serialized = serialize($embedding_vector);
763
764 // Update the prompt in the database
765 $updated = $wpdb->update(
766 $table_name,
767 array(
768 'article_content' => $article_content,
769 'embedding_vector' => $embedding_vector_serialized,
770 'source_url' => $article_url,
771 ),
772 array('id' => $prompt_id),
773 array('%s', '%s', '%s'),
774 array('%d')
775 );
776
777 if ($updated !== false) {
778 wp_send_json_success();
779 } else {
780 wp_send_json_error('Database update failed.');
781 }
782 } else {
783 wp_send_json_error('Embedding generation failed.');
784 }
785 } else {
786 wp_send_json_error('Invalid data.');
787 }
788 }
789
790
791 public function mxchat_fetch_chat_history() {
792 global $wpdb;
793 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
794
795 // Check if the current user has sufficient permissions
796 if (!current_user_can('manage_options')) {
797 echo esc_html__('You do not have sufficient permissions to view this page.', 'mxchat');
798 wp_die();
799 }
800
801 // Fetch chat transcripts from the database, ordered by timestamp
802 $chat_transcripts = $wpdb->get_results(
803 $wpdb->prepare("SELECT * FROM {$table_name} ORDER BY timestamp ASC")
804 );
805
806 // If no transcripts are available, display a message
807 if (empty($chat_transcripts)) {
808 echo esc_html__('No chat history available.', 'mxchat');
809 wp_die();
810 }
811
812 ob_start();
813 $current_session_id = '';
814
815 foreach ($chat_transcripts as $transcript) {
816 // Start a new session block if session ID changes
817 if ($current_session_id !== $transcript->session_id) {
818 if ($current_session_id !== '') {
819 echo '</div>'; // Close the previous session block
820 }
821 $current_session_id = sanitize_text_field($transcript->session_id);
822 echo '<div class="chat-session">';
823 echo '<h4><input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($transcript->session_id) . '"> ' . esc_html__('Session ID:', 'mxchat') . ' ' . esc_html($current_session_id) . '</h4>';
824 }
825
826 // Format the timestamp for display
827 $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
828
829 // Determine the role to display (user identifier or email for users, bot for the AI)
830 $role = $transcript->role;
831 if ($role === 'user') {
832 // Sanitize email if available
833 if (!empty($transcript->user_email)) {
834 $role = sanitize_email($transcript->user_email);
835 } else {
836 // Sanitize user identifier, anonymize if it's an IP address
837 $user_identifier = sanitize_text_field($transcript->user_identifier);
838
839 // Check if the user identifier is an IP address and anonymize it
840 if (filter_var($user_identifier, FILTER_VALIDATE_IP)) {
841 $role = preg_replace('/\.\d+$/', '.xxx', $user_identifier); // Mask the last octet
842 } else {
843 $role = $user_identifier; // If it's not an IP, just display the identifier
844 }
845 }
846 }
847
848 // Output the chat message
849 echo '<div class="chat-message">';
850 echo '<strong>' . esc_html($role) . ' (' . esc_html($formatted_timestamp) . '):</strong> ';
851 echo wp_kses_post($transcript->message);
852 echo '</div>';
853 }
854
855 // Close the final session block
856 echo '</div>';
857
858 $output = ob_get_clean();
859
860 echo $output;
861 wp_die();
862 }
863
864
865 public function mxchat_create_activation_page() {
866 $license_status = get_option('mxchat_license_status', 'inactive');
867 $license_error = get_option('mxchat_license_error', '');
868
869 ?>
870 <div class="wrap mxchat-admin">
871 <h2>MxChat Pro: Activation</h2>
872 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
873 <div class="error notice">
874 <p><?php echo esc_html($license_error); ?></p>
875 </div>
876 <?php endif; ?>
877 <form id="mxchat-activation-form">
878 <table class="form-table">
879 <tr valign="top">
880 <th scope="row">Email Address</th>
881 <td>
882 <input type="email" id="mxchat_pro_email" name="mxchat_pro_email" value="<?php echo esc_attr(get_option('mxchat_pro_email')); ?>" class="regular-text" />
883 </td>
884 </tr>
885 <tr valign="top">
886 <th scope="row">Activation Key</th>
887 <td>
888 <input type="text" id="mxchat_activation_key" name="mxchat_activation_key" value="<?php echo esc_attr(get_option('mxchat_activation_key')); ?>" class="regular-text" />
889 </td>
890 </tr>
891 </table>
892 <?php if ($license_status !== 'active'): ?>
893 <?php submit_button('Activate License', 'primary', 'activate_license'); ?>
894 <?php else: ?>
895 <h3>MxChat Pro</h3>
896 <?php endif; ?>
897 </form>
898 <h3>License Status: <span id="mxchat-license-status"><?php echo $license_status === 'active' ? 'Active' : 'Inactive'; ?></span></h3>
899 </div>
900 <?php
901 }
902
903 public function mxchat_intents_page_html() {
904 if (!current_user_can('manage_options')) {
905 return;
906 }
907
908 // Handle form submissions
909 if (isset($_POST['mxchat_add_intent']) && check_admin_referer('mxchat_add_intent_nonce')) {
910 $this->mxchat_handle_add_intent();
911 } elseif (isset($_POST['mxchat_delete_intent']) && check_admin_referer('mxchat_delete_intent_nonce')) {
912 $this->mxchat_handle_delete_intent();
913 }
914
915 // Fetch existing intents with pagination and filtering
916 global $wpdb;
917 $table_name = $wpdb->prefix . 'mxchat_intents';
918 $page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
919 $per_page = 20;
920 $offset = ($page - 1) * $per_page;
921
922 // Build the WHERE clause based on filters
923 $where = '1=1';
924 $search_term = isset($_GET['s']) ? trim($_GET['s']) : '';
925 $callback_filter = isset($_GET['callback_filter']) ? sanitize_text_field($_GET['callback_filter']) : '';
926
927 if ($search_term) {
928 $search_term_like = '%' . $wpdb->esc_like($search_term) . '%';
929 $where .= $wpdb->prepare(' AND (intent_label LIKE %s OR phrases LIKE %s)', $search_term_like, $search_term_like);
930 }
931
932 if ($callback_filter) {
933 $where .= $wpdb->prepare(' AND callback_function = %s', $callback_filter);
934 }
935
936 // Get total count for pagination
937 $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
938 $total_pages = ceil($total_intents / $per_page);
939
940 // Fetch intents with pagination and filters
941 $intents = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d", $per_page, $offset));
942
943 // Get available callback functions with 'pro_only' flag
944 $available_callbacks = $this->mxchat_get_available_callbacks();
945
946 ?>
947
948 <div class="wrap mxchat-admin">
949 <form method="post">
950 <?php wp_nonce_field('mxchat_add_intent_nonce'); ?>
951 <h2><?php esc_html_e('Add New Intent', 'mxchat'); ?></h2>
952 <!-- Documentation encouragement message -->
953 <p class="description">
954 <?php esc_html_e('We highly encourage users to quickly read our ', 'mxchat'); ?>
955 <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
956 <?php esc_html_e('documentation', 'mxchat'); ?>
957 </a>
958 <?php esc_html_e(' to better understand intents. Some intents require setup in the Integration tab.', 'mxchat'); ?>
959 </p>
960
961 <div class="mxchat-form-group">
962 <label for="intent_label"><?php esc_html_e('Intent Label', 'mxchat'); ?></label>
963 <input name="intent_label" type="text" id="intent_label" class="regular-text" required>
964 </div>
965 <div class="mxchat-form-group">
966 <label for="phrases"><?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?></label>
967 <textarea name="phrases" id="phrases" rows="5" class="large-text" required></textarea>
968 </div>
969 <div class="mxchat-form-group">
970 <label for="callback_function"><?php esc_html_e('Callback Function', 'mxchat'); ?></label>
971 <select name="callback_function" id="callback_function" required>
972 <option value=""><?php esc_html_e('Select a Callback', 'mxchat'); ?></option>
973 <?php foreach ($available_callbacks as $function => $callback_data): ?>
974 <?php
975 $label = $callback_data['label'];
976 $pro_only = $callback_data['pro_only'];
977 // Disable if intent is Pro-only and user is not activated
978 $disabled = (!$this->is_activated && $pro_only) ? 'disabled' : '';
979 $label_suffix = (!$this->is_activated && $pro_only) ? ' (Pro Only)' : '';
980 ?>
981 <option value="<?php echo esc_attr($function); ?>" <?php echo $disabled; ?>>
982 <?php echo esc_html($label . $label_suffix); ?>
983 </option>
984 <?php endforeach; ?>
985 </select>
986 </div>
987 <button type="submit" name="mxchat_add_intent" class="button button-primary submit-content-button">
988 <?php esc_html_e('Add Intent', 'mxchat'); ?>
989 </button>
990
991 <!-- Loader and loading text (initially hidden) -->
992 <div id="mxchat-intent-loading" class="mxchat-spinner" style="display: none;"></div>
993 <div id="mxchat-loading-text-intent" style="display: none; margin-top: 10px; color: #333;">
994 <?php esc_html_e('Loading, please wait...', 'mxchat'); ?>
995 </div>
996 </form>
997
998 <!-- Filter Form -->
999 <form method="get" action="">
1000 <input type="hidden" name="page" value="mxchat-intents">
1001 <div class="mxchat-search-group">
1002 <input type="text" name="s" id="mxchat-intent-search" placeholder="<?php esc_attr_e('Search Intents', 'mxchat'); ?>" value="<?php echo esc_attr($search_term); ?>">
1003 <select name="callback_filter" id="mxchat-callback-filter">
1004 <option value=""><?php esc_html_e('All Callbacks', 'mxchat'); ?></option>
1005 <?php foreach ($available_callbacks as $function => $callback_data): ?>
1006 <?php $label = $callback_data['label']; ?>
1007 <option value="<?php echo esc_attr($function); ?>" <?php selected($callback_filter, $function); ?>><?php echo esc_html($label); ?></option>
1008 <?php endforeach; ?>
1009 </select>
1010 <button type="submit" class="button"><?php esc_html_e('Filter', 'mxchat'); ?></button>
1011 </div>
1012 </form>
1013
1014 <!-- Intents Table -->
1015 <table class="wp-list-table mxchat-table widefat fixed striped">
1016 <thead>
1017 <tr>
1018 <th><?php esc_html_e('Intent Label', 'mxchat'); ?></th>
1019 <th><?php esc_html_e('Phrases', 'mxchat'); ?></th>
1020 <th><?php esc_html_e('Callback Function', 'mxchat'); ?></th>
1021 <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
1022 </tr>
1023 </thead>
1024 <tbody>
1025 <?php if ($intents): ?>
1026 <?php foreach ($intents as $intent): ?>
1027 <?php
1028 $callback_function = $intent->callback_function;
1029 $callback_label = isset($available_callbacks[$callback_function]['label']) ? $available_callbacks[$callback_function]['label'] : $callback_function;
1030 ?>
1031 <tr>
1032 <td><?php echo esc_html($intent->intent_label); ?></td>
1033 <td><?php echo esc_html($intent->phrases); ?></td>
1034 <td><?php echo esc_html($callback_label); ?></td>
1035 <td>
1036 <form method="post" onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this intent?', 'mxchat'); ?>');">
1037 <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
1038 <input type="hidden" name="intent_id" value="<?php echo esc_attr($intent->id); ?>">
1039 <button type="submit" name="mxchat_delete_intent" class="button button-secondary"><?php esc_html_e('Delete', 'mxchat'); ?></button>
1040 </form>
1041 </td>
1042 </tr>
1043 <?php endforeach; ?>
1044 <?php else: ?>
1045 <tr>
1046 <td colspan="4"><?php esc_html_e('No intents found.', 'mxchat'); ?></td>
1047 </tr>
1048 <?php endif; ?>
1049 </tbody>
1050 </table>
1051
1052 <!-- Pagination Links -->
1053 <?php if ($total_pages > 1): ?>
1054 <div class="pagination mxchat-pagination">
1055 <?php
1056 $base_url = admin_url('admin.php?page=mxchat-intents');
1057 $query_args = [];
1058 if ($search_term) {
1059 $query_args['s'] = $search_term;
1060 }
1061 if ($callback_filter) {
1062 $query_args['callback_filter'] = $callback_filter;
1063 }
1064
1065 for ($i = 1; $i <= $total_pages; $i++):
1066 $query_args['paged'] = $i;
1067 $page_url = add_query_arg($query_args, $base_url);
1068 ?>
1069 <a href="<?php echo esc_url($page_url); ?>" class="page-number <?php if ($i == $page) echo 'current-page'; ?>"><?php echo $i; ?></a>
1070 <?php endfor; ?>
1071 </div>
1072 <?php endif; ?>
1073
1074 </div>
1075
1076 <?php
1077 }
1078
1079 public function mxchat_handle_add_intent() {
1080 ob_start(); // Start output buffering
1081
1082 check_admin_referer('mxchat_add_intent_nonce');
1083
1084 global $wpdb;
1085 $table_name = $wpdb->prefix . 'mxchat_intents';
1086
1087 // Get the form input
1088 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
1089 $phrases_input = isset($_POST['phrases']) ? $_POST['phrases'] : '';
1090 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
1091
1092 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
1093 ob_end_clean(); // End output buffering and discard output
1094 wp_die(esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
1095 }
1096
1097 // Get available callback functions
1098 $available_callbacks = $this->mxchat_get_available_callbacks();
1099
1100 // Validate callback function
1101 if (!array_key_exists($callback_function, $available_callbacks)) {
1102 ob_end_clean(); // End output buffering and discard output
1103 wp_die(esc_html__('Invalid callback function selected.', 'mxchat'));
1104 }
1105
1106 // Check if the callback is Pro-only and the user is not activated
1107 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
1108 if ($is_pro_only && !$this->is_activated) {
1109 ob_end_clean(); // End output buffering and discard output
1110 wp_die(esc_html__('This callback function is available in the Pro version only.', 'mxchat'));
1111 }
1112
1113 // Split and sanitize phrases
1114 $phrases_array = array_map('trim', explode(',', $phrases_input));
1115 $phrases_array = array_filter($phrases_array); // Remove empty phrases
1116 $phrases_array = array_map('sanitize_text_field', $phrases_array);
1117
1118 if (empty($phrases_array)) {
1119 ob_end_clean(); // End output buffering and discard output
1120 wp_die(esc_html__('Please enter at least one valid phrase.', 'mxchat'));
1121 }
1122
1123 // Generate and collect embeddings for each phrase
1124 $vectors = [];
1125 foreach ($phrases_array as $phrase) {
1126 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
1127 if (is_array($embedding_vector)) {
1128 $vectors[] = $embedding_vector;
1129 } else {
1130 ob_end_clean(); // End output buffering and discard output
1131 wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase));
1132 }
1133 }
1134
1135 // Average the vectors to create a single embedding vector for the intent
1136 if (!empty($vectors)) {
1137 $combined_vector = $this->mxchat_average_vectors($vectors);
1138 $serialized_vector = maybe_serialize($combined_vector);
1139
1140 // Insert the combined vector as a single entry for the intent
1141 $result = $wpdb->insert($table_name, [
1142 'intent_label' => $intent_label,
1143 'phrases' => implode(', ', $phrases_array),
1144 'embedding_vector' => $serialized_vector,
1145 'callback_function' => $callback_function,
1146 ]);
1147
1148 if ($result === false) {
1149 ob_end_clean(); // End output buffering and discard output
1150 wp_die(esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error));
1151 }
1152
1153 ob_end_clean(); // End output buffering and discard output
1154
1155 // Set a transient to handle the redirect after output processing
1156 set_transient('mxchat_intent_added_redirect', true, 5 * MINUTE_IN_SECONDS);
1157 return; // Stop the function without a redirect
1158 } else {
1159 ob_end_clean(); // End output buffering and discard output
1160 wp_die(esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
1161 }
1162 }
1163
1164
1165
1166 private function mxchat_get_available_callbacks() {
1167 return [
1168 'mxchat_handle_email_capture' => [
1169 'label' => 'Email Capture',
1170 'pro_only' => false,
1171 ],
1172 'mxchat_handle_product_inquiry' => [
1173 'label' => 'Show Product Card',
1174 'pro_only' => true,
1175 ],
1176 'mxchat_handle_order_history' => [
1177 'label' => 'Order History',
1178 'pro_only' => true,
1179 ],
1180 'mxchat_generate_image' => [
1181 'label' => 'Generate Image',
1182 'pro_only' => true,
1183 ],
1184 'mxchat_handle_search_request' => [
1185 'label' => 'Brave Web Search',
1186 'pro_only' => false,
1187 ],
1188 'mxchat_handle_image_search_request' => [
1189 'label' => 'Brave Image Search',
1190 'pro_only' => false,
1191 ],
1192 'mxchat_handle_add_to_cart_intent' => [
1193 'label' => 'Add to Cart',
1194 'pro_only' => true,
1195 ],
1196 'mxchat_handle_checkout_intent' => [
1197 'label' => 'Proceed to Checkout',
1198 'pro_only' => true,
1199 ],
1200 ];
1201 }
1202
1203
1204 private function mxchat_average_vectors($vectors) {
1205 $vector_length = count($vectors[0]);
1206 $sum_vector = array_fill(0, $vector_length, 0);
1207
1208 foreach ($vectors as $vector) {
1209 for ($i = 0; $i < $vector_length; $i++) {
1210 $sum_vector[$i] += $vector[$i];
1211 }
1212 }
1213
1214 // Divide each component by the number of vectors to get the average
1215 $num_vectors = count($vectors);
1216 for ($i = 0; $i < $vector_length; $i++) {
1217 $sum_vector[$i] /= $num_vectors;
1218 }
1219
1220 return $sum_vector;
1221 }
1222
1223 public function mxchat_handle_delete_intent() {
1224 check_admin_referer('mxchat_delete_intent_nonce');
1225
1226 if (isset($_POST['intent_id'])) {
1227 global $wpdb;
1228 $table_name = $wpdb->prefix . 'mxchat_intents';
1229 $intent_id = intval($_POST['intent_id']);
1230
1231 // Delete the intent from the database
1232 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
1233 }
1234 }
1235
1236 public function mxchat_page_init() {
1237 // Register settings
1238 register_setting(
1239 'mxchat_option_group',
1240 'mxchat_options',
1241 array($this, 'mxchat_sanitize')
1242 );
1243
1244 // Chatbot Settings Section
1245 add_settings_section(
1246 'mxchat_chatbot_section',
1247 'Chatbot Settings',
1248 null,
1249 'mxchat-chatbot'
1250 );
1251
1252 // Registering fields for the Chatbot Settings section
1253 add_settings_field(
1254 'api_key',
1255 'OpenAI API Key',
1256 array($this, 'api_key_callback'),
1257 'mxchat-chatbot',
1258 'mxchat_chatbot_section'
1259 );
1260
1261 add_settings_field(
1262 'xai_api_key',
1263 'X.AI API Key',
1264 array($this, 'xai_api_key_callback'),
1265 'mxchat-chatbot',
1266 'mxchat_chatbot_section'
1267 );
1268
1269 add_settings_field(
1270 'claude_api_key',
1271 'Claude API Key',
1272 array($this, 'claude_api_key_callback'),
1273 'mxchat-chatbot',
1274 'mxchat_chatbot_section'
1275 );
1276
1277
1278 add_settings_field(
1279 'system_prompt_instructions',
1280 'AI Instructions',
1281 array($this, 'system_prompt_instructions_callback'),
1282 'mxchat-chatbot',
1283 'mxchat_chatbot_section'
1284 );
1285
1286 add_settings_field(
1287 'model',
1288 'Model',
1289 array($this, 'mxchat_model_callback'),
1290 'mxchat-chatbot',
1291 'mxchat_chatbot_section'
1292 );
1293
1294 add_settings_field(
1295 'top_bar_title',
1296 'Top Bar Title',
1297 array($this, 'mxchat_top_bar_title_callback'),
1298 'mxchat-chatbot',
1299 'mxchat_chatbot_section'
1300 );
1301
1302 add_settings_field(
1303 'intro_message',
1304 'Introductory Message',
1305 array($this, 'mxchat_intro_message_callback'),
1306 'mxchat-chatbot',
1307 'mxchat_chatbot_section'
1308 );
1309
1310 add_settings_field(
1311 'input_copy',
1312 'Input Copy',
1313 array($this, 'mxchat_input_copy_callback'),
1314 'mxchat-chatbot',
1315 'mxchat_chatbot_section'
1316 );
1317
1318 add_settings_field(
1319 'rate_limit',
1320 'Rate Limit',
1321 array($this, 'mxchat_rate_limit_callback'),
1322 'mxchat-chatbot',
1323 'mxchat_chatbot_section'
1324 );
1325
1326 add_settings_field(
1327 'rate_limit_message',
1328 'Rate Limit Message',
1329 array($this, 'mxchat_rate_limit_message_callback'),
1330 'mxchat-chatbot',
1331 'mxchat_chatbot_section'
1332 );
1333
1334 add_settings_field(
1335 'pre_chat_message',
1336 'Pre-Chat Message',
1337 array($this, 'mxchat_pre_chat_message_callback'),
1338 'mxchat-chatbot',
1339 'mxchat_chatbot_section'
1340 );
1341
1342 add_settings_field(
1343 'append_to_body',
1344 'Append Chat Widget to Body',
1345 array($this, 'mxchat_append_to_body_callback'),
1346 'mxchat-chatbot',
1347 'mxchat_chatbot_section'
1348 );
1349
1350 add_settings_field(
1351 'privacy_toggle',
1352 'Toggle Privacy Notice',
1353 array($this, 'mxchat_privacy_toggle_callback'),
1354 'mxchat-chatbot',
1355 'mxchat_chatbot_section'
1356 );
1357
1358 add_settings_field(
1359 'complianz_toggle',
1360 'Enable Complianz',
1361 array($this, 'mxchat_complianz_toggle_callback'),
1362 'mxchat-chatbot',
1363 'mxchat_chatbot_section'
1364 );
1365
1366 add_settings_field(
1367 'link_target_toggle',
1368 'Open Links in a New Tab',
1369 array($this, 'mxchat_link_target_toggle_callback'),
1370 'mxchat-chatbot',
1371 'mxchat_chatbot_section'
1372 );
1373
1374 add_settings_field(
1375 'chat_persistence_toggle',
1376 'Enable Chat Persistence',
1377 array($this, 'mxchat_chat_persistence_toggle_callback'),
1378 'mxchat-chatbot',
1379 'mxchat_chatbot_section'
1380 );
1381
1382 add_settings_field(
1383 'popular_question_1',
1384 'Popular Question 1',
1385 array($this, 'mxchat_popular_question_1_callback'),
1386 'mxchat-chatbot',
1387 'mxchat_chatbot_section'
1388 );
1389
1390 add_settings_field(
1391 'popular_question_2',
1392 'Popular Question 2',
1393 array($this, 'mxchat_popular_question_2_callback'),
1394 'mxchat-chatbot',
1395 'mxchat_chatbot_section'
1396 );
1397
1398 add_settings_field(
1399 'popular_question_3',
1400 'Popular Question 3',
1401 array($this, 'mxchat_popular_question_3_callback'),
1402 'mxchat-chatbot',
1403 'mxchat_chatbot_section'
1404 );
1405
1406
1407
1408 // WooCommerce Settings Section
1409 add_settings_section(
1410 'mxchat_woocommerce_section',
1411 'WooCommerce Settings',
1412 null,
1413 'mxchat-embed'
1414 );
1415
1416 // Loops Settings Section
1417 add_settings_section(
1418 'mxchat_loops_section',
1419 'Loops Settings',
1420 null,
1421 'mxchat-embed'
1422 );
1423
1424 // WooCommerce Settings Fields
1425 add_settings_field(
1426 'enable_woocommerce_integration',
1427 'Automatically Embed Products',
1428 array($this, 'mxchat_enable_woocommerce_integration_callback'),
1429 'mxchat-embed',
1430 'mxchat_woocommerce_section'
1431 );
1432
1433
1434 add_settings_field(
1435 'woocommerce_consumer_key',
1436 'WooCommerce Consumer Key',
1437 array($this, 'mxchat_woocommerce_consumer_key_callback'),
1438 'mxchat-embed',
1439 'mxchat_woocommerce_section'
1440 );
1441
1442 add_settings_field(
1443 'woocommerce_consumer_secret',
1444 'WooCommerce Consumer Secret',
1445 array($this, 'mxchat_woocommerce_consumer_secret_callback'),
1446 'mxchat-embed',
1447 'mxchat_woocommerce_section'
1448 );
1449
1450 // Loops Settings Fields
1451 add_settings_field(
1452 'loops_api_key',
1453 'Loops API Key',
1454 array($this, 'mxchat_loops_api_key_callback'),
1455 'mxchat-embed',
1456 'mxchat_loops_section'
1457 );
1458
1459 add_settings_field(
1460 'loops_mailing_list',
1461 'Loops Mailing List',
1462 array($this, 'mxchat_loops_mailing_list_callback'),
1463 'mxchat-embed',
1464 'mxchat_loops_section'
1465 );
1466
1467 add_settings_field(
1468 'triggered_phrase_response',
1469 'Triggered Phrase Response',
1470 array($this, 'mxchat_triggered_phrase_response_callback'),
1471 'mxchat-embed',
1472 'mxchat_loops_section'
1473 );
1474
1475 add_settings_field(
1476 'email_capture_response',
1477 'Email Capture Response',
1478 array($this, 'mxchat_email_capture_response_callback'),
1479 'mxchat-embed',
1480 'mxchat_loops_section'
1481 );
1482
1483
1484 // Brave Search Settings Fields
1485 add_settings_section(
1486 'mxchat_brave_section',
1487 __('Brave Search Settings', 'mxchat'),
1488 array($this, 'mxchat_brave_section_callback'),
1489 'mxchat-embed'
1490 );
1491
1492 add_settings_field(
1493 'brave_api_key',
1494 __('Brave API Key', 'mxchat'),
1495 array($this, 'mxchat_brave_api_key_callback'),
1496 'mxchat-embed',
1497 'mxchat_brave_section'
1498 );
1499
1500 add_settings_field(
1501 'brave_image_count',
1502 __('Number of Images to Return', 'mxchat'),
1503 array($this, 'mxchat_brave_image_count_callback'),
1504 'mxchat-embed',
1505 'mxchat_brave_section'
1506 );
1507
1508 add_settings_field(
1509 'brave_safe_search',
1510 __('Safe Search', 'mxchat'),
1511 array($this, 'mxchat_brave_safe_search_callback'),
1512 'mxchat-embed',
1513 'mxchat_brave_section'
1514 );
1515
1516 add_settings_field(
1517 'brave_news_count',
1518 __('Number of News Articles', 'mxchat'),
1519 array($this, 'mxchat_brave_news_count_callback'),
1520 'mxchat-embed',
1521 'mxchat_brave_section'
1522 );
1523
1524 add_settings_field(
1525 'brave_country',
1526 __('Country', 'mxchat'),
1527 array($this, 'mxchat_brave_country_callback'),
1528 'mxchat-embed',
1529 'mxchat_brave_section'
1530 );
1531
1532 add_settings_field(
1533 'brave_language',
1534 __('Language', 'mxchat'),
1535 array($this, 'mxchat_brave_language_callback'),
1536 'mxchat-embed',
1537 'mxchat_brave_section'
1538 );
1539
1540
1541
1542 // Theme Settings Section
1543 add_settings_section(
1544 'mxchat_theme_section',
1545 'Theme Settings',
1546 null,
1547 'mxchat-theme'
1548 );
1549
1550 add_settings_field(
1551 'close_button_color',
1552 'Close Button & Title Color',
1553 array($this, 'mxchat_close_button_color_callback'),
1554 'mxchat-theme',
1555 'mxchat_theme_section'
1556 );
1557
1558 add_settings_field(
1559 'chatbot_bg_color',
1560 'Chatbot Background Color',
1561 array($this, 'mxchat_chatbot_bg_color_callback'),
1562 'mxchat-theme',
1563 'mxchat_theme_section'
1564 );
1565
1566 add_settings_field(
1567 'user_message_bg_color',
1568 'User Message Background Color',
1569 array($this, 'mxchat_user_message_bg_color_callback'),
1570 'mxchat-theme',
1571 'mxchat_theme_section'
1572 );
1573
1574 add_settings_field(
1575 'user_message_font_color',
1576 'User Message Font Color',
1577 array($this, 'mxchat_user_message_font_color_callback'),
1578 'mxchat-theme',
1579 'mxchat_theme_section'
1580 );
1581
1582 add_settings_field(
1583 'bot_message_bg_color',
1584 'Bot Message Background Color',
1585 array($this, 'mxchat_bot_message_bg_color_callback'),
1586 'mxchat-theme',
1587 'mxchat_theme_section'
1588 );
1589
1590 add_settings_field(
1591 'bot_message_font_color',
1592 'Bot Message Font Color',
1593 array($this, 'mxchat_bot_message_font_color_callback'),
1594 'mxchat-theme',
1595 'mxchat_theme_section'
1596 );
1597
1598 add_settings_field(
1599 'top_bar_bg_color',
1600 'Top Bar Background Color',
1601 array($this, 'mxchat_top_bar_bg_color_callback'),
1602 'mxchat-theme',
1603 'mxchat_theme_section'
1604 );
1605
1606 add_settings_field(
1607 'send_button_font_color',
1608 'Send Button Color',
1609 array($this, 'mxchat_send_button_font_color_callback'),
1610 'mxchat-theme',
1611 'mxchat_theme_section'
1612 );
1613
1614 add_settings_field(
1615 'chat_input_font_color',
1616 'Chat Input Font Color',
1617 array($this, 'mxchat_chat_input_font_color_callback'),
1618 'mxchat-theme',
1619 'mxchat_theme_section'
1620 );
1621
1622 add_settings_field(
1623 'chatbot_background_color',
1624 'Floating Widget Background Color',
1625 array($this, 'mxchat_chatbot_background_color_callback'),
1626 'mxchat-theme',
1627 'mxchat_theme_section'
1628 );
1629
1630 add_settings_field(
1631 'icon_color',
1632 'Chatbot Icon Color',
1633 array($this, 'mxchat_icon_color_callback'),
1634 'mxchat-theme',
1635 'mxchat_theme_section'
1636 );
1637
1638 // General Settings Section
1639 add_settings_section(
1640 'mxchat_general_section',
1641 'Frequently Asked Questions (FAQ)',
1642 null,
1643 'mxchat-general'
1644 );
1645
1646
1647 }
1648
1649 public function mxchat_handle_activate_license() {
1650 check_ajax_referer('mxchat_activate_license_nonce', 'security');
1651
1652 $license_key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : '';
1653 $customer_email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
1654
1655 if (empty($license_key) || empty($customer_email)) {
1656 wp_send_json_error('Email or License Key is missing');
1657 }
1658
1659 $product_id = 'MxChatPRO';
1660
1661 $response = wp_remote_get("http://mxchat.ai/?wc-api=software-api&request=activation&email={$customer_email}&license_key={$license_key}&product_id={$product_id}");
1662
1663 if (is_wp_error($response)) {
1664 wp_send_json_error('Activation failed due to a server error');
1665 }
1666
1667 $body = wp_remote_retrieve_body($response);
1668 $data = json_decode($body);
1669
1670 if ($data && isset($data->activated) && $data->activated) {
1671 update_option('mxchat_license_status', 'active');
1672 wp_send_json_success();
1673 } else {
1674 $error_message = isset($data->error) ? $data->error : 'Activation failed';
1675 update_option('mxchat_license_status', 'inactive');
1676 update_option('mxchat_license_error', $error_message);
1677 wp_send_json_error($error_message);
1678 }
1679 }
1680
1681 public function mxchat_rate_limit_callback() {
1682 $rate_limits = array('5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' here
1683 $selected_rate_limit = isset($this->options['rate_limit']) ? $this->options['rate_limit'] : '100';
1684
1685 echo '<div class="pro-feature-wrapper active">';
1686 echo '<select id="rate_limit" name="mxchat_options[rate_limit]">';
1687 foreach ($rate_limits as $limit) {
1688 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
1689 }
1690 echo '</select>';
1691 echo '</div>';
1692 }
1693
1694 public function mxchat_rate_limit_message_callback() {
1695 // Remove the is_activated check and make it always enabled
1696 echo '<div class="pro-feature-wrapper active">';
1697 printf(
1698 '<textarea id="rate_limit_message" name="mxchat_options[rate_limit_message]" rows="3" cols="50">%s</textarea>',
1699 isset($this->options['rate_limit_message']) ? esc_textarea($this->options['rate_limit_message']) : 'Rate limit exceeded. Please try again later.'
1700 );
1701 echo '</div>';
1702 }
1703
1704
1705 public function mxchat_enable_woocommerce_integration_callback() {
1706 $checked = isset($this->options['enable_woocommerce_integration']) && $this->options['enable_woocommerce_integration'] === '1' ? 'checked' : '';
1707 $disabled = $this->is_activated ? '' : 'disabled';
1708 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1709
1710 echo '<div class="' . esc_attr($class) . '">';
1711 echo '<label class="toggle-switch">';
1712 echo '<input type="checkbox" id="enable_woocommerce_integration" name="mxchat_options[enable_woocommerce_integration]" value="1" ' . $checked . ' ' . $disabled . '>';
1713 echo '<span class="slider"></span>';
1714 echo '</label>';
1715
1716 if (!$this->is_activated) {
1717 echo '<div class="pro-feature-overlay">';
1718 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1719 echo '</div>';
1720 }
1721
1722 echo '</div>';
1723 }
1724
1725
1726
1727
1728 private function mxchat_add_option_field($id, $title, $callback = '') {
1729 add_settings_field(
1730 $id,
1731 $title,
1732 $callback ? array($this, $callback) : array($this, $id . '_callback'),
1733 'mxchat-max',
1734 'mxchat_setting_section_id',
1735 $id === 'model' ? ['label_for' => 'model'] : []
1736 );
1737 }
1738
1739
1740 public function api_key_callback() {
1741 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
1742 echo '<input type="password" id="api_key" name="mxchat_options[api_key]" value="' . $apiKey . '" class="regular-text" />';
1743 echo '<button type="button" id="toggleApiKeyVisibility">Show</button>';
1744 }
1745
1746 public function xai_api_key_callback() {
1747 // Check if the feature is activated (paid feature)
1748 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
1749 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
1750
1751 // Render the input field for the X.AI API key
1752 echo '<div class="' . esc_attr($class) . '">';
1753 printf(
1754 '<input type="password" id="xai_api_key" name="mxchat_options[xai_api_key]" value="%s" class="regular-text" %s />',
1755 isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '',
1756 $disabled
1757 );
1758 echo '<button type="button" id="toggleXaiApiKeyVisibility">Show</button>';
1759
1760 // If the feature is not activated, show the overlay with a "Pro Only" message
1761 if (!$this->is_activated) {
1762 echo '<div class="pro-feature-overlay">';
1763 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1764 echo '</div>';
1765 }
1766
1767 echo '</div>';
1768 }
1769
1770 public function claude_api_key_callback() {
1771 // Check if the feature is activated (paid feature)
1772 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
1773 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
1774
1775 // Render the input field for the Claude API key
1776 echo '<div class="' . esc_attr($class) . '">';
1777 printf(
1778 '<input type="password" id="claude_api_key" name="mxchat_options[claude_api_key]" value="%s" class="regular-text" %s />',
1779 isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '',
1780 $disabled
1781 );
1782 echo '<button type="button" id="toggleClaudeApiKeyVisibility">Show</button>';
1783
1784 // If the feature is not activated, show the overlay with a "Pro Only" message
1785 if (!$this->is_activated) {
1786 echo '<div class="pro-feature-overlay">';
1787 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1788 echo '</div>';
1789 }
1790
1791 echo '</div>';
1792 }
1793
1794 public function mxchat_woocommerce_consumer_key_callback() {
1795 $disabled = $this->is_activated ? '' : 'disabled';
1796 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1797
1798 echo '<div class="' . esc_attr($class) . '">';
1799 printf(
1800 '<input type="text" id="woocommerce_consumer_key" name="mxchat_options[woocommerce_consumer_key]" value="%s" class="regular-text" %s />',
1801 isset($this->options['woocommerce_consumer_key']) ? esc_attr($this->options['woocommerce_consumer_key']) : '',
1802 $disabled
1803 );
1804
1805 if (!$this->is_activated) {
1806 echo '<div class="pro-feature-overlay">';
1807 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1808 echo '</div>';
1809 }
1810
1811 echo '</div>';
1812 }
1813
1814 public function mxchat_woocommerce_consumer_secret_callback() {
1815 $disabled = $this->is_activated ? '' : 'disabled';
1816 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1817
1818 echo '<div class="' . esc_attr($class) . '">';
1819 printf(
1820 '<input type="password" id="woocommerce_consumer_secret" name="mxchat_options[woocommerce_consumer_secret]" value="%s" class="regular-text" %s />',
1821 isset($this->options['woocommerce_consumer_secret']) ? esc_attr($this->options['woocommerce_consumer_secret']) : '',
1822 $disabled
1823 );
1824 echo '<button type="button" id="toggleWooCommerceSecretVisibility">Show</button>';
1825
1826 if (!$this->is_activated) {
1827 echo '<div class="pro-feature-overlay">';
1828 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1829 echo '</div>';
1830 }
1831
1832 echo '</div>';
1833 }
1834
1835 public function mxchat_loops_api_key_callback() {
1836 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
1837
1838 echo '<div class="api-key-wrapper">';
1839 printf(
1840 '<input type="password" id="loops_api_key" name="mxchat_options[loops_api_key]" value="%s" class="regular-text" />',
1841 $loops_api_key
1842 );
1843 echo '<button type="button" id="toggleLoopsApiKeyVisibility">Show</button>';
1844 echo '</div>';
1845 echo '<p class="description">Enter your Loops API Key here. (See FAQ for details)</p>';
1846 }
1847
1848 public function mxchat_loops_mailing_list_callback() {
1849 // Retrieve Loops API key and lists
1850 $loops_api_key = isset($this->options['loops_api_key']) ? $this->options['loops_api_key'] : '';
1851 $selected_list = isset($this->options['loops_mailing_list']) ? $this->options['loops_mailing_list'] : '';
1852
1853 if ($loops_api_key) {
1854 // Fetch lists from Loops API
1855 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
1856
1857 if ($lists) {
1858 echo '<select id="loops_mailing_list" name="mxchat_options[loops_mailing_list]">';
1859 foreach ($lists as $list) {
1860 echo '<option value="' . esc_attr($list['id']) . '" ' . selected($selected_list, $list['id'], false) . '>' . esc_html($list['name']) . '</option>';
1861 }
1862 echo '</select>';
1863 } else {
1864 echo '<p class="description">No lists found. Please verify your API Key.</p>';
1865 }
1866 } else {
1867 echo '<p class="description">Enter a valid Loops API Key to load mailing lists.</p>';
1868 }
1869 }
1870
1871 public function mxchat_triggered_phrase_response_callback() {
1872 $triggered_response = isset($this->options['triggered_phrase_response']) ? $this->options['triggered_phrase_response'] : '';
1873 echo '<textarea id="triggered_phrase_response" name="mxchat_options[triggered_phrase_response]" rows="3" cols="50">' . esc_textarea($triggered_response) . '</textarea>';
1874 echo '<p class="description">Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.</p>';
1875 }
1876
1877 public function mxchat_email_capture_response_callback() {
1878 $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.';
1879 echo '<textarea id="email_capture_response" name="mxchat_options[email_capture_response]" rows="3" cols="50">' . esc_textarea($email_capture_response) . '</textarea>';
1880 echo '<p class="description">Enter the message to send when a user provides their email.</p>';
1881 }
1882
1883
1884 public function mxchat_pre_chat_message_callback() {
1885 printf(
1886 '<textarea id="pre_chat_message" name="mxchat_options[pre_chat_message]" rows="5" cols="50">%s</textarea>',
1887 isset($this->options['pre_chat_message']) ? esc_textarea($this->options['pre_chat_message']) : ''
1888 );
1889 }
1890
1891 // Callback for AI Instructions textarea
1892 public function system_prompt_instructions_callback() {
1893 printf(
1894 '<textarea id="system_prompt_instructions" name="mxchat_options[system_prompt_instructions]" rows="5" cols="50">%s</textarea>',
1895 isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : ''
1896 );
1897 }
1898
1899 public function mxchat_model_callback() {
1900 $models = array(
1901 'X.AI Models' => array(
1902 'grok-beta' => 'grok-beta (Early Beta)'
1903 ),
1904 'Claude Models' => array(
1905 'claude-3-5-sonnet-20241022' => 'Claude 3.5 Sonnet (Most Intelligent)',
1906 'claude-3-opus-20240229' => 'Claude 3 Opus (Highly Complex Tasks)',
1907 'claude-3-sonnet-20240229' => 'Claude 3 Sonnet (Balanced)',
1908 'claude-3-haiku-20240307' => 'Claude 3 Haiku (Fastest)'
1909 ),
1910 'OpenAI Models' => array(
1911 'gpt-4o' => 'GPT-4o (Recommended)',
1912 'gpt-4o-mini' => 'GPT-4o Mini (Fast and Lightweight)',
1913 'gpt-4-turbo' => 'GPT-4 Turbo (High-Performance)',
1914 'gpt-4' => 'GPT-4 (High Intelligence)',
1915 'gpt-3.5-turbo' => 'GPT-3.5 Turbo (Affordable and Fast)'
1916 )
1917 );
1918
1919 $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo';
1920
1921 echo '<select id="model" name="mxchat_options[model]">';
1922
1923 foreach ($models as $group_label => $group_models) {
1924 echo '<optgroup label="' . esc_attr($group_label) . '">';
1925
1926 foreach ($group_models as $model_value => $model_label) {
1927 // Disable if not activated for paid models (Claude or X.AI)
1928 $disabled = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? 'disabled' : '';
1929 $label_suffix = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? ' (Pro Only)' : '';
1930
1931 // Output the <option> element
1932 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
1933 }
1934
1935 echo '</optgroup>';
1936 }
1937
1938 echo '</select>';
1939 }
1940
1941
1942
1943 public function mxchat_top_bar_title_callback() {
1944 printf(
1945 '<input type="text" id="top_bar_title" name="mxchat_options[top_bar_title]" value="%s" />',
1946 isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : ''
1947 );
1948 }
1949
1950 public function mxchat_intro_message_callback() {
1951 printf(
1952 '<textarea id="intro_message" name="mxchat_options[intro_message]" rows="5" cols="50">%s</textarea>',
1953 isset($this->options['intro_message']) ? esc_textarea($this->options['intro_message']) : 'Hello! How can I assist you today?'
1954 );
1955 }
1956
1957 public function mxchat_input_copy_callback() {
1958 printf(
1959 '<input type="text" id="input_copy" name="mxchat_options[input_copy]" value="%s" placeholder="How can I assist?" />',
1960 isset($this->options['input_copy']) ? esc_attr($this->options['input_copy']) : 'How can I assist?'
1961 );
1962 echo '<p class="description">This is the placeholder text for the chat input field.</p>';
1963 }
1964
1965
1966
1967
1968
1969
1970 public function mxchat_close_button_color_callback() {
1971 $disabled = $this->is_activated ? '' : 'disabled';
1972
1973 echo '<div class="pro-feature-wrapper">';
1974 printf(
1975 '<input type="text" id="close_button_color" name="mxchat_options[close_button_color]" value="%s" class="my-color-field" data-default-color="#4a4a4a" %s />',
1976 isset($this->options['close_button_color']) ? esc_attr($this->options['close_button_color']) : '',
1977 esc_attr($disabled)
1978 );
1979
1980 if (!$this->is_activated) {
1981 echo '<div class="pro-feature-overlay">';
1982 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1983 echo '</div>';
1984 }
1985
1986 echo '</div>';
1987 }
1988
1989 public function mxchat_chatbot_bg_color_callback() {
1990 $disabled = $this->is_activated ? '' : 'disabled';
1991
1992 echo '<div class="pro-feature-wrapper">';
1993 printf(
1994 '<input type="text" id="chatbot_bg_color" name="mxchat_options[chatbot_bg_color]" value="%s" class="my-color-field" data-default-color="#f9f9f9" %s />',
1995 isset($this->options['chatbot_bg_color']) ? esc_attr($this->options['chatbot_bg_color']) : '',
1996 esc_attr($disabled)
1997 );
1998
1999 if (!$this->is_activated) {
2000 echo '<div class="pro-feature-overlay">';
2001 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2002 echo '</div>';
2003 }
2004
2005 echo '</div>';
2006 }
2007
2008 public function mxchat_user_message_bg_color_callback() {
2009 $disabled = $this->is_activated ? '' : 'disabled';
2010
2011 echo '<div class="pro-feature-wrapper">';
2012 printf(
2013 '<input type="text" id="user_message_bg_color" name="mxchat_options[user_message_bg_color]" value="%s" class="my-color-field" data-default-color="#0078d7" %s />',
2014 isset($this->options['user_message_bg_color']) ? esc_attr($this->options['user_message_bg_color']) : '',
2015 esc_attr($disabled)
2016 );
2017
2018 if (!$this->is_activated) {
2019 echo '<div class="pro-feature-overlay">';
2020 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2021 echo '</div>';
2022 }
2023
2024 echo '</div>';
2025 }
2026
2027 public function mxchat_user_message_font_color_callback() {
2028 $disabled = $this->is_activated ? '' : 'disabled';
2029
2030 echo '<div class="pro-feature-wrapper">';
2031 printf(
2032 '<input type="text" id="user_message_font_color" name="mxchat_options[user_message_font_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2033 isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '',
2034 esc_attr($disabled)
2035 );
2036
2037 if (!$this->is_activated) {
2038 echo '<div class="pro-feature-overlay">';
2039 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2040 echo '</div>';
2041 }
2042
2043 echo '</div>';
2044 }
2045
2046 public function mxchat_bot_message_bg_color_callback() {
2047 $disabled = $this->is_activated ? '' : 'disabled';
2048
2049 echo '<div class="pro-feature-wrapper">';
2050 printf(
2051 '<input type="text" id="bot_message_bg_color" name="mxchat_options[bot_message_bg_color]" value="%s" class="my-color-field" data-default-color="#e1e1e1" %s />',
2052 isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '',
2053 esc_attr($disabled)
2054 );
2055
2056 if (!$this->is_activated) {
2057 echo '<div class="pro-feature-overlay">';
2058 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2059 echo '</div>';
2060 }
2061
2062 echo '</div>';
2063 }
2064
2065 public function mxchat_bot_message_font_color_callback() {
2066 $disabled = $this->is_activated ? '' : 'disabled';
2067
2068 echo '<div class="pro-feature-wrapper">';
2069 printf(
2070 '<input type="text" id="bot_message_font_color" name="mxchat_options[bot_message_font_color]" value="%s" class="my-color-field" data-default-color="#333333" %s />',
2071 isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '',
2072 esc_attr($disabled)
2073 );
2074
2075 if (!$this->is_activated) {
2076 echo '<div class="pro-feature-overlay">';
2077 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2078 echo '</div>';
2079 }
2080
2081 echo '</div>';
2082 }
2083
2084 public function mxchat_top_bar_bg_color_callback() {
2085 $disabled = $this->is_activated ? '' : 'disabled';
2086
2087 echo '<div class="pro-feature-wrapper">';
2088 printf(
2089 '<input type="text" id="top_bar_bg_color" name="mxchat_options[top_bar_bg_color]" value="%s" class="my-color-field" data-default-color="#00b294" %s />',
2090 isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '',
2091 esc_attr($disabled)
2092 );
2093
2094 if (!$this->is_activated) {
2095 echo '<div class="pro-feature-overlay">';
2096 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2097 echo '</div>';
2098 }
2099
2100 echo '</div>';
2101 }
2102
2103 public function mxchat_send_button_font_color_callback() {
2104 $disabled = $this->is_activated ? '' : 'disabled';
2105
2106 echo '<div class="pro-feature-wrapper">';
2107 printf(
2108 '<input type="text" id="send_button_font_color" name="mxchat_options[send_button_font_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2109 isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '',
2110 esc_attr($disabled)
2111 );
2112
2113 if (!$this->is_activated) {
2114 echo '<div class="pro-feature-overlay">';
2115 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2116 echo '</div>';
2117 }
2118
2119 echo '</div>';
2120 }
2121
2122 public function mxchat_chatbot_background_color_callback() {
2123 $disabled = $this->is_activated ? '' : 'disabled';
2124
2125 echo '<div class="pro-feature-wrapper">';
2126 printf(
2127 '<input type="text" id="chatbot_background_color" name="mxchat_options[chatbot_background_color]" value="%s" class="my-color-field" data-default-color="#000000" %s />',
2128 isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '',
2129 esc_attr($disabled)
2130 );
2131
2132 if (!$this->is_activated) {
2133 echo '<div class="pro-feature-overlay">';
2134 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2135 echo '</div>';
2136 }
2137
2138 echo '</div>';
2139 }
2140
2141 public function mxchat_icon_color_callback() {
2142 $disabled = $this->is_activated ? '' : 'disabled';
2143
2144 echo '<div class="pro-feature-wrapper">';
2145 printf(
2146 '<input type="text" id="icon_color" name="mxchat_options[icon_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2147 isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '',
2148 esc_attr($disabled)
2149 );
2150
2151 if (!$this->is_activated) {
2152 echo '<div class="pro-feature-overlay">';
2153 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2154 echo '</div>';
2155 }
2156
2157 echo '</div>';
2158 }
2159
2160 public function mxchat_chat_input_font_color_callback() {
2161 $disabled = $this->is_activated ? '' : 'disabled';
2162
2163 echo '<div class="pro-feature-wrapper">';
2164 printf(
2165 '<input type="text" id="chat_input_font_color" name="mxchat_options[chat_input_font_color]" value="%s" class="my-color-field" data-default-color="#555555" %s />',
2166 isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '',
2167 esc_attr($disabled)
2168 );
2169
2170 if (!$this->is_activated) {
2171 echo '<div class="pro-feature-overlay">';
2172 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2173 echo '</div>';
2174 }
2175
2176 echo '</div>';
2177 }
2178
2179
2180 public function mxchat_append_to_body_callback() {
2181 $append_to_body_checked = isset($this->options['append_to_body']) && $this->options['append_to_body'] === 'on' ? 'checked' : '';
2182 echo '<label class="toggle-switch">';
2183 printf(
2184 '<input type="checkbox" id="append_to_body" name="mxchat_options[append_to_body]" %s />',
2185 esc_attr($append_to_body_checked)
2186 );
2187 echo '<span class="slider"></span>';
2188 echo '</label>';
2189 echo '<p class="description">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"].</p>';
2190 }
2191
2192
2193 public function mxchat_privacy_toggle_callback() {
2194 // Check if the privacy toggle is enabled
2195 $privacy_toggle_checked = isset($this->options['privacy_toggle']) && $this->options['privacy_toggle'] === 'on' ? 'checked' : '';
2196
2197 // Retrieve the stored privacy text if it exists
2198 $privacy_text = isset($this->options['privacy_text']) ? wp_kses_post($this->options['privacy_text']) : 'By chatting, you agree to our <a href="https://example.com/privacy-policy" target="_blank">privacy policy</a>.';
2199
2200 // Output the toggle switch
2201 echo '<label class="toggle-switch">';
2202 printf(
2203 '<input type="checkbox" id="privacy_toggle" name="mxchat_options[privacy_toggle]" %s />',
2204 esc_attr($privacy_toggle_checked)
2205 );
2206 echo '<span class="slider"></span>';
2207 echo '</label>';
2208 echo '<p class="description">Enable this option to display a privacy notice below the chat widget.</p>';
2209
2210 // Output the custom text input field
2211 printf(
2212 '<textarea id="privacy_text" name="mxchat_options[privacy_text]" rows="5" cols="50" class="regular-text">%s</textarea>',
2213 esc_textarea($privacy_text)
2214 );
2215 echo '<p class="description">Enter the privacy policy text. You can include HTML links.</p>';
2216 }
2217
2218
2219 public function mxchat_complianz_toggle_callback() {
2220 // Check if the Complianz toggle is enabled
2221 $complianz_toggle_checked = isset($this->options['complianz_toggle']) && $this->options['complianz_toggle'] === 'on' ? 'checked' : '';
2222
2223 // Check if the plugin is activated (paid feature)
2224 $disabled = $this->is_activated ? '' : 'disabled';
2225 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2226
2227 echo '<div class="' . esc_attr($class) . '">';
2228
2229 // Output the toggle switch
2230 echo '<label class="toggle-switch">';
2231 printf(
2232 '<input type="checkbox" id="complianz_toggle" name="mxchat_options[complianz_toggle]" %s %s />',
2233 esc_attr($complianz_toggle_checked),
2234 esc_attr($disabled)
2235 );
2236 echo '<span class="slider"></span>';
2237 echo '</label>';
2238 echo '<p class="description">Enable this option to apply Complianz consent logic to the chatbot.</p>';
2239
2240 // If the feature is not activated, show the Pro feature overlay
2241 if (!$this->is_activated) {
2242 echo '<div class="pro-feature-overlay">';
2243 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2244 echo '</div>';
2245 }
2246
2247 echo '</div>';
2248 }
2249
2250
2251
2252
2253
2254 public function mxchat_link_target_toggle_callback() {
2255 // Check if the toggle is enabled in the options
2256 $link_target_toggle = isset($this->options['link_target_toggle']) && $this->options['link_target_toggle'] === 'on' ? 'checked' : '';
2257
2258 // Output the toggle switch
2259 echo '<label class="toggle-switch">';
2260 printf(
2261 '<input type="checkbox" id="link_target_toggle" name="mxchat_options[link_target_toggle]" %s />',
2262 esc_attr($link_target_toggle)
2263 );
2264 echo '<span class="slider"></span>';
2265 echo '</label>';
2266 echo '<p class="description">Enable to open links in a new tab (default is to open in the same tab).</p>';
2267 }
2268
2269 public function mxchat_chat_persistence_toggle_callback() {
2270 // Check if chat persistence toggle is enabled
2271 $chat_persistence_toggle_checked = isset($this->options['chat_persistence_toggle']) && $this->options['chat_persistence_toggle'] === 'on' ? 'checked' : '';
2272
2273 // Check if the plugin is activated (paid feature)
2274 $disabled = $this->is_activated ? '' : 'disabled';
2275 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2276
2277 echo '<div class="' . esc_attr($class) . '">';
2278
2279 // Output the toggle switch
2280 echo '<label class="toggle-switch">';
2281 printf(
2282 '<input type="checkbox" id="chat_persistence_toggle" name="mxchat_options[chat_persistence_toggle]" %s %s />',
2283 esc_attr($chat_persistence_toggle_checked),
2284 esc_attr($disabled)
2285 );
2286 echo '<span class="slider"></span>';
2287 echo '</label>';
2288 echo '<p class="description">Enable to keep chat history when users navigate tabs or return to the site within 24 hours.</p>';
2289
2290 // If not activated, show Pro feature overlay
2291 if (!$this->is_activated) {
2292 echo '<div class="pro-feature-overlay">';
2293 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2294 echo '</div>';
2295 }
2296
2297 echo '</div>';
2298 }
2299
2300
2301 public function mxchat_popular_question_1_callback() {
2302 printf(
2303 '<input type="text" id="popular_question_1" name="mxchat_options[popular_question_1]" value="%s" placeholder="Enter Popular Question 1" />',
2304 isset($this->options['popular_question_1']) ? esc_attr($this->options['popular_question_1']) : ''
2305 );
2306 echo '<p class="description">This will be the first popular question in the chatbot.</p>';
2307 }
2308
2309 public function mxchat_popular_question_2_callback() {
2310 // Check if the plugin is activated (paid feature)
2311 $disabled = $this->is_activated ? '' : 'disabled';
2312 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2313
2314 echo '<div class="' . esc_attr($class) . '">';
2315
2316 printf(
2317 '<input type="text" id="popular_question_2" name="mxchat_options[popular_question_2]" value="%s" placeholder="Enter Popular Question 2" %s />',
2318 isset($this->options['popular_question_2']) ? esc_attr($this->options['popular_question_2']) : '',
2319 esc_attr($disabled)
2320 );
2321 echo '<p class="description">This will be the second popular question in the chatbot.</p>';
2322
2323 // If not activated, show Pro feature overlay
2324 if (!$this->is_activated) {
2325 echo '<div class="pro-feature-overlay">';
2326 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2327 echo '</div>';
2328 }
2329
2330 echo '</div>';
2331 }
2332
2333 public function mxchat_popular_question_3_callback() {
2334 // Check if the plugin is activated (paid feature)
2335 $disabled = $this->is_activated ? '' : 'disabled';
2336 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2337
2338 echo '<div class="' . esc_attr($class) . '">';
2339
2340 printf(
2341 '<input type="text" id="popular_question_3" name="mxchat_options[popular_question_3]" value="%s" placeholder="Enter Popular Question 3" %s />',
2342 isset($this->options['popular_question_3']) ? esc_attr($this->options['popular_question_3']) : '',
2343 esc_attr($disabled)
2344 );
2345 echo '<p class="description">This will be the third popular question in the chatbot.</p>';
2346
2347 // If not activated, show Pro feature overlay
2348 if (!$this->is_activated) {
2349 echo '<div class="pro-feature-overlay">';
2350 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2351 echo '</div>';
2352 }
2353
2354 echo '</div>';
2355 }
2356
2357
2358 /**
2359 * Callback for Brave API Key field.
2360 */
2361 public function mxchat_brave_api_key_callback() {
2362 $options = get_option('mxchat_options');
2363 $brave_api_key = isset($options['brave_api_key']) ? esc_attr($options['brave_api_key']) : '';
2364
2365 echo '<div class="api-key-wrapper">';
2366 printf(
2367 '<input type="password" id="brave_api_key" name="mxchat_options[brave_api_key]" value="%s" class="regular-text" />',
2368 $brave_api_key
2369 );
2370 echo '<button type="button" id="toggleBraveApiKeyVisibility" class="button-secondary">Show</button>';
2371 echo '</div>';
2372 echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
2373
2374 ?>
2375 <?php
2376 }
2377
2378 /**
2379 * Callback for Number of Images to Return field.
2380 */
2381 public function mxchat_brave_image_count_callback() {
2382 $options = get_option('mxchat_options');
2383 $brave_image_count = isset($options['brave_image_count']) ? intval($options['brave_image_count']) : 4;
2384
2385 printf(
2386 '<input type="number" id="brave_image_count" name="mxchat_options[brave_image_count]" value="%d" min="1" max="6" />',
2387 $brave_image_count
2388 );
2389 echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
2390 }
2391
2392 /**
2393 * Callback for Safe Search field.
2394 */
2395 public function mxchat_brave_safe_search_callback() {
2396 $options = get_option('mxchat_options');
2397 $brave_safe_search = isset($options['brave_safe_search']) ? esc_attr($options['brave_safe_search']) : 'strict';
2398
2399 ?>
2400 <select id="brave_safe_search" name="mxchat_options[brave_safe_search]">
2401 <option value="strict" <?php selected($brave_safe_search, 'strict'); ?>><?php _e('Strict', 'mxchat'); ?></option>
2402 <option value="off" <?php selected($brave_safe_search, 'off'); ?>><?php _e('Off', 'mxchat'); ?></option>
2403 </select>
2404 <p class="description"><?php _e('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat'); ?></p>
2405 <?php
2406 }
2407
2408
2409 /**
2410 * Callback for Number of News Articles field.
2411 */
2412 public function mxchat_brave_news_count_callback() {
2413 $options = get_option('mxchat_options');
2414 $brave_news_count = isset($options['brave_news_count']) ? intval($options['brave_news_count']) : 3;
2415
2416 printf(
2417 '<input type="number" id="brave_news_count" name="mxchat_options[brave_news_count]" value="%d" min="1" max="10" />',
2418 $brave_news_count
2419 );
2420 echo '<p class="description">' . __('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
2421 }
2422
2423 /**
2424 * Callback for Country field.
2425 */
2426 public function mxchat_brave_country_callback() {
2427 $options = get_option('mxchat_options');
2428 $brave_country = isset($options['brave_country']) ? esc_attr($options['brave_country']) : 'us';
2429
2430 printf(
2431 '<input type="text" id="brave_country" name="mxchat_options[brave_country]" value="%s" maxlength="2" class="small-text" />',
2432 $brave_country
2433 );
2434 echo '<p class="description">' . __('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
2435 }
2436
2437 /**
2438 * Callback for Language field.
2439 */
2440 public function mxchat_brave_language_callback() {
2441 $options = get_option('mxchat_options');
2442 $brave_language = isset($options['brave_language']) ? esc_attr($options['brave_language']) : 'en';
2443
2444 printf(
2445 '<input type="text" id="brave_language" name="mxchat_options[brave_language]" value="%s" maxlength="2" class="small-text" />',
2446 $brave_language
2447 );
2448 echo '<p class="description">' . __('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
2449 }
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459 public function mxchat_enqueue_admin_assets() {
2460 wp_enqueue_style('wp-color-picker');
2461
2462 // Get the plugin version or file modification time for cache busting
2463 $plugin_version = '1.3'; // Replace this with your plugin's version
2464
2465 // File paths
2466 $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
2467 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
2468 $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
2469 $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
2470
2471 // Check if files exist and get modification times
2472 $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
2473 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
2474 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
2475 $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
2476
2477 // Enqueue scripts and styles with corrected paths
2478 wp_enqueue_script(
2479 'mxchat-color-picker',
2480 plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
2481 array('wp-color-picker'),
2482 $color_picker_version,
2483 true
2484 );
2485
2486 wp_enqueue_script(
2487 'mxchat-embedding-check',
2488 plugin_dir_url(__FILE__) . '../js/embedding-check.js',
2489 array(),
2490 $embedding_check_version,
2491 true
2492 );
2493
2494 wp_enqueue_script(
2495 'mxchat-transcripts-js',
2496 plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
2497 array('jquery'),
2498 $transcripts_js_version,
2499 true
2500 );
2501
2502 wp_enqueue_script(
2503 'mxchat-admin-js',
2504 plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
2505 array('jquery'),
2506 $plugin_version,
2507 true
2508 );
2509
2510 wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
2511 'ajax_url' => admin_url('admin-ajax.php'),
2512 'nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
2513 ));
2514
2515 wp_enqueue_style(
2516 'mxchat-admin-css',
2517 plugin_dir_url(__FILE__) . '../css/admin-style.css',
2518 array(),
2519 $admin_css_version
2520 );
2521
2522 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
2523 'ajax_url' => admin_url('admin-ajax.php'),
2524 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
2525 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
2526 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
2527 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
2528 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
2529 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
2530 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
2531 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
2532 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
2533 'icon_color' => $this->options['icon_color'] ?? '#fff',
2534 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
2535 'pre_chat_message' => $this->options['pre_chat_message'] ?? 'Hey there! Ask me anything!',
2536 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
2537
2538 // New fields for Loops Integration
2539 'loops_api_key' => $this->options['loops_api_key'] ?? '',
2540 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
2541 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? 'Would you like to join our mailing list? Please provide your email below.',
2542 'email_capture_response' => $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.'
2543 ));
2544
2545 // Localize the script to pass the nonce and other data to JavaScript
2546 wp_localize_script('mxchat-admin-js', 'mxchatInlineEdit', array(
2547 'nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
2548 'ajax_url' => admin_url('admin-ajax.php')
2549 ));
2550
2551
2552 }
2553
2554 public function mxchat_sanitize($input) {
2555 $new_input = array();
2556
2557 if (isset($input['api_key'])) {
2558 $new_input['api_key'] = sanitize_text_field($input['api_key']);
2559 }
2560
2561 if (isset($input['xai_api_key'])) {
2562 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
2563 }
2564
2565 if (isset($input['claude_api_key'])) {
2566 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
2567 }
2568
2569 if (isset($input['enable_woocommerce_integration'])) {
2570 $new_input['enable_woocommerce_integration'] = isset($input['enable_woocommerce_integration']) && $input['enable_woocommerce_integration'] === '1' ? '1' : '0';
2571
2572 }
2573
2574 if (isset($input['privacy_toggle'])) {
2575 $new_input['privacy_toggle'] = $input['privacy_toggle'];
2576 }
2577
2578 if (isset($input['complianz_toggle'])) {
2579 $new_input['complianz_toggle'] = $input['complianz_toggle'];
2580 }
2581
2582 // Handle custom privacy text input
2583 if (isset($input['privacy_text'])) {
2584 // Allow basic HTML for links
2585 $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
2586 }
2587
2588 if (isset($input['system_prompt_instructions'])) {
2589 $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
2590 }
2591
2592 if (isset($input['mxchat_pro_email'])) {
2593 $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
2594 }
2595
2596 if (isset($input['mxchat_activation_key'])) {
2597 $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
2598 }
2599
2600 if (isset($input['append_to_body'])) {
2601 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
2602 }
2603
2604 if (isset($input['top_bar_title'])) {
2605 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
2606 }
2607
2608 if (isset($input['intro_message'])) {
2609 $new_input['intro_message'] = sanitize_text_field($input['intro_message']);
2610 }
2611
2612 if (isset($input['input_copy'])) {
2613 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
2614 }
2615
2616
2617 if (isset($input['rate_limit_message'])) {
2618 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
2619 }
2620
2621 if (isset($input['rate_limit'])) {
2622 $allowed_limits = array('5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
2623 $rate_limit = sanitize_text_field($input['rate_limit']);
2624
2625 if (in_array($rate_limit, $allowed_limits, true)) {
2626 $new_input['rate_limit'] = $rate_limit;
2627 } else {
2628 // Set a default or fallback value in case of an invalid entry
2629 $new_input['rate_limit'] = '100';
2630 }
2631 }
2632
2633
2634 if (isset($input['pre_chat_message'])) {
2635 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
2636 }
2637
2638 if (isset($input['model'])) {
2639 $allowed_models = array(
2640 'grok-beta',
2641 'claude-3-5-sonnet-20241022',
2642 'claude-3-opus-20240229',
2643 'claude-3-sonnet-20240229',
2644 'claude-3-haiku-20240307',
2645 'gpt-4o',
2646 'gpt-4o-mini',
2647 'gpt-4-turbo',
2648 'gpt-4',
2649 'gpt-3.5-turbo',
2650 );
2651 if (in_array($input['model'], $allowed_models)) {
2652 $new_input['model'] = sanitize_text_field($input['model']);
2653 }
2654 }
2655
2656 // Sanitize new pro features
2657 if (isset($input['close_button_color'])) {
2658 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
2659 }
2660
2661 if (isset($input['chatbot_bg_color'])) {
2662 $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
2663 }
2664
2665 if (isset($input['woocommerce_consumer_key'])) {
2666 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
2667 }
2668
2669 if (isset($input['woocommerce_consumer_secret'])) {
2670 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
2671 }
2672
2673 if (isset($input['user_message_bg_color'])) {
2674 $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
2675 }
2676
2677 if (isset($input['user_message_font_color'])) {
2678 $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
2679 }
2680
2681 if (isset($input['bot_message_bg_color'])) {
2682 $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
2683 }
2684
2685 if (isset($input['bot_message_font_color'])) {
2686 $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
2687 }
2688
2689 if (isset($input['top_bar_bg_color'])) {
2690 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
2691 }
2692
2693 if (isset($input['send_button_font_color'])) {
2694 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
2695 }
2696
2697 if (isset($input['chatbot_background_color'])) {
2698 $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
2699 }
2700
2701 if (isset($input['icon_color'])) {
2702 $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
2703 }
2704
2705 if (isset($input['chat_input_font_color'])) {
2706 $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
2707 }
2708
2709 // Sanitize link_target_toggle
2710 if (isset($input['link_target_toggle'])) {
2711 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
2712 }
2713
2714 // Sanitize Loops API Key
2715 if (isset($input['loops_api_key'])) {
2716 $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
2717 }
2718
2719 if (isset($input['chat_persistence_toggle'])) {
2720 $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
2721 }
2722
2723
2724
2725 if (isset($input['popular_question_1'])) {
2726 $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
2727 }
2728
2729 if (isset($input['popular_question_2'])) {
2730 $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
2731 }
2732
2733 if (isset($input['popular_question_3'])) {
2734 $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
2735 }
2736
2737
2738 // Sanitize Loops Mailing List
2739 if (isset($input['loops_mailing_list'])) {
2740 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
2741 }
2742
2743 // Sanitize Triggered Phrase Response
2744 if (isset($input['triggered_phrase_response'])) {
2745 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
2746 }
2747
2748 if (isset($input['email_capture_response'])) {
2749 $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
2750 }
2751
2752
2753 // Sanitize Brave Search Settings
2754 if (isset($input['brave_api_key'])) {
2755 $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
2756 }
2757
2758 if (isset($input['brave_image_count'])) {
2759 $image_count = intval($input['brave_image_count']);
2760 $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
2761 }
2762
2763 if (isset($input['brave_safe_search'])) {
2764 $allowed = array('strict', 'off');
2765 $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
2766 }
2767
2768 if (isset($input['brave_news_count'])) {
2769 $news_count = intval($input['brave_news_count']);
2770 $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
2771 }
2772
2773 if (isset($input['brave_country'])) {
2774 $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
2775 }
2776
2777 if (isset($input['brave_language'])) {
2778 $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
2779 }
2780
2781
2782
2783
2784
2785
2786
2787 return $new_input;
2788 }
2789
2790
2791 // Method to append the chatbot to the body
2792 public function mxchat_append_chatbot_to_body() {
2793 $options = get_option('mxchat_options');
2794 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
2795 echo do_shortcode('[mxchat_chatbot floating="yes"]');
2796 }
2797 }
2798
2799
2800
2801 private function mxchat_extract_main_content($html) {
2802 $dom = new DOMDocument;
2803 libxml_use_internal_errors(true); // Suppress HTML parsing errors
2804 @$dom->loadHTML($html);
2805 libxml_clear_errors();
2806
2807 $xpath = new DOMXPath($dom);
2808
2809 // Simplified selectors focusing on common content areas
2810 $selectors = [
2811 '//article',
2812 '//*[@id="content"]',
2813 '//*[@class="entry-content"]',
2814 '//main',
2815 ];
2816
2817 foreach ($selectors as $selector) {
2818 $nodes = $xpath->query($selector);
2819 if ($nodes->length > 0) {
2820 $content = '';
2821 foreach ($nodes as $node) {
2822 $content .= $dom->saveHTML($node);
2823 }
2824 return $content;
2825 }
2826 }
2827
2828 // Fallback: Return the entire body content if no specific selector matches
2829 $body = $dom->getElementsByTagName('body');
2830 return $body->length > 0 ? $dom->saveHTML($body->item(0)) : $html;
2831 }
2832
2833 public function mxchat_handle_sitemap_submission() {
2834 // Check if the form was submitted and the user has sufficient permissions
2835 if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
2836 return;
2837 }
2838
2839 // Verify the nonce field for security
2840 check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
2841
2842 // Sanitize and retrieve the submitted URL
2843 $submitted_url = esc_url_raw($_POST['sitemap_url']); // Accept either a sitemap URL or a regular URL
2844
2845 // Fetch the content of the submitted URL
2846 $response = wp_remote_get($submitted_url);
2847 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
2848 set_transient('mxchat_admin_notice_error', 'Failed to fetch the URL. Please check the URL and try again.', 30);
2849 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2850 exit;
2851 }
2852
2853 $content_type = wp_remote_retrieve_header($response, 'content-type');
2854 $body_content = wp_remote_retrieve_body($response);
2855
2856 // Check if the URL points to a sitemap (XML) or a regular HTML page
2857 if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
2858 // Handle Sitemap XML
2859 $xml = simplexml_load_string($body_content);
2860 if ($xml === false) {
2861 set_transient('mxchat_admin_notice_error', 'Invalid sitemap XML. Please provide a valid sitemap.', 30);
2862 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2863 exit;
2864 }
2865
2866 $embedding_success = true; // Flag to track if all embeddings are successful
2867
2868 foreach ($xml->url as $url_element) {
2869 $page_url = (string)$url_element->loc;
2870
2871 $page_response = wp_remote_get($page_url);
2872 if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
2873 continue;
2874 }
2875
2876 $page_html = wp_remote_retrieve_body($page_response);
2877 $page_content = $this->mxchat_extract_main_content($page_html);
2878 $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
2879
2880 if (!empty($sanitized_content)) {
2881 $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
2882 if (is_array($embedding_vector)) {
2883 MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $this->options['api_key']);
2884 } else {
2885 $embedding_success = false; // Set flag to false if any embedding fails
2886 }
2887 }
2888 }
2889
2890 if ($embedding_success) {
2891 set_transient('mxchat_admin_notice_success', 'Sitemap content successfully submitted!', 30);
2892 } else {
2893 set_transient('mxchat_admin_notice_error', 'Some content failed to embed. Please check your API key and try again.', 30);
2894 }
2895
2896 } else {
2897 // Handle Regular URL (HTML Page)
2898 $page_content = $this->mxchat_extract_main_content($body_content);
2899 $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
2900
2901 if (!empty($sanitized_content)) {
2902 $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
2903 if (is_array($embedding_vector)) {
2904 MxChat_Utils::submit_content_to_db($sanitized_content, $submitted_url, $this->options['api_key']);
2905 set_transient('mxchat_admin_notice_success', 'URL content successfully submitted!', 30);
2906 } else {
2907 set_transient('mxchat_admin_notice_error', 'Failed to generate embedding for the URL content. Please check your API key and try again.', 30);
2908 }
2909 } else {
2910 set_transient('mxchat_admin_notice_error', 'No valid content found on the provided URL.', 30);
2911 }
2912 }
2913
2914 // Redirect after setting the transient
2915 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2916 exit;
2917 }
2918
2919
2920
2921 private function mxchat_sanitize_content_for_api($content) {
2922 // Remove script, style tags, and HTML comments
2923 $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
2924 $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
2925 $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
2926
2927 // Remove all HTML tags and decode HTML entities
2928 $content = wp_strip_all_tags($content);
2929 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
2930
2931 // Trim and normalize whitespace
2932 $content = trim(preg_replace('/\s+/', ' ', $content));
2933
2934 return $content;
2935 }
2936
2937
2938
2939 private function mxchat_fetch_loops_mailing_lists($api_key) {
2940 $url = 'https://app.loops.so/api/v1/lists';
2941 $response = wp_remote_get($url, array(
2942 'headers' => array(
2943 'Authorization' => 'Bearer ' . $api_key,
2944 'Content-Type' => 'application/json'
2945 )
2946 ));
2947
2948 if (is_wp_error($response)) {
2949 return array();
2950 }
2951
2952 $body = wp_remote_retrieve_body($response);
2953 $lists = json_decode($body, true);
2954
2955 return isset($lists) && is_array($lists) ? $lists : array();
2956 }
2957
2958
2959
2960
2961
2962 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
2963 if (empty($vec1) || empty($vec2)) {
2964 return 0.0;
2965 }
2966
2967 $dot_product = 0.0;
2968 $norm_a = 0.0;
2969 $norm_b = 0.0;
2970
2971 for ($i = 0; $i < count($vec1); $i++) {
2972 $dot_product += $vec1[$i] * $vec2[$i];
2973 $norm_a += pow($vec1[$i], 2);
2974 $norm_b += pow($vec2[$i], 2);
2975 }
2976
2977 if ($norm_a == 0.0 || $norm_b == 0.0) {
2978 return 0.0;
2979 } else {
2980 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
2981 }
2982 }
2983
2984
2985
2986
2987 }
2988 ?>
2989