PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.5.7
MxChat – AI Chatbot & Content Generation for WordPress v1.5.7
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.5.7, at includes/class-mxchat-admin.php

4,623 lines 184.8 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 add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
39
40 // Define admin_post actions for form submission handling
41 add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
42 add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent'));
43 add_action('admin_post_mxchat_update_intent_threshold', array($this, 'mxchat_handle_update_intent_threshold'));
44 add_action('admin_post_mxchat_stop_processing', array($this, 'mxchat_stop_processing'));
45 add_action('admin_post_mxchat_edit_intent', array($this, 'handle_edit_intent'));
46 }
47
48 // Method to check if the license is active
49 private function is_license_active() {
50 $license_status = get_option('mxchat_license_status', 'inactive');
51 return $license_status === 'active';
52 }
53
54 // Initialize default options
55 private function initialize_default_options() {
56 $default_options = array(
57 'api_key' => '',
58 'xai_api_key' => '',
59 'claude_api_key' => '',
60 '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:
61 - Your name is [Chatbot Name].
62 - 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.
63 - Do not make up links to pages. Only use specific links you see in your knowledgebase.
64 - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
65 - 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.',
66 'model' => 'gpt-4o',
67 'rate_limit_logged_in' => '100',
68 'rate_limit_logged_out' => '100',
69 'rate_limit_message' => 'Rate limit exceeded. Please try again later.',
70 'top_bar_title' => 'MxChat',
71 'intro_message' => 'Hello! How can I assist you today?',
72 'input_copy' => 'How can I assist?',
73 'append_to_body' => 'off',
74 'close_button_color' => '#fff',
75 'chatbot_bg_color' => '#fff',
76 'user_message_bg_color' => '#fff',
77 'user_message_font_color' => '#212121',
78 'bot_message_bg_color' => '#212121',
79 'bot_message_font_color' => '#fff',
80 'top_bar_bg_color' => '#212121',
81 'send_button_font_color' => '#212121',
82 'chat_input_font_color' => '#212121',
83 'chatbot_background_color' => '#212121',
84 'icon_color' => '#fff',
85 'enable_woocommerce_integration' => '0',
86 'link_target_toggle' => 'off',
87 'pre_chat_message' => 'Hey there! Ask me anything!',
88
89 // New fields for Loops Integration
90 'loops_api_key' => '',
91 'loops_mailing_list' => '',
92 'triggered_phrase_response' => 'Would you like to join our mailing list? Please provide your email below.',
93 'email_capture_response' => 'Thank you for providing your email! You\'ve been added to our list.',
94 'popular_question_1' => '',
95 'popular_question_2' => '',
96 'popular_question_3' => '',
97 'pdf_intent_trigger_text' => "Please provide the URL to the PDF you'd like to discuss.",
98 'pdf_intent_success_text' => "I've processed the PDF. What questions do you have about it?",
99 'pdf_intent_error_text' => "Sorry, I couldn't process the PDF. Please ensure it's a valid file.",
100 'pdf_max_pages' => 69,
101
102 // Live Agent Integration
103 'live_agent_webhook_url' => '',
104 'live_agent_secret_key' => '',
105 'live_agent_bot_token' => '',
106 'live_agent_message_bg_color' => '#ffffff',
107 'live_agent_message_font_color' => '#333333',
108 'chat_toolbar_toggle' => 'off',
109 'mode_indicator_bg_color' => '#767676',
110 'mode_indicator_font_color' => '#ffffff',
111 'toolbar_icon_color' => '#212121',
112 );
113
114
115 // Merge existing options with defaults
116 $existing_options = get_option('mxchat_options', array());
117 $merged_options = wp_parse_args($existing_options, $default_options);
118
119 // Update the options if they have changed
120 if ($existing_options !== $merged_options) {
121 update_option('mxchat_options', $merged_options);
122 }
123
124 // Update the $this->options property
125 $this->options = $merged_options;
126 }
127
128
129
130 public function mxchat_add_plugin_page() {
131 // Main menu page
132 add_menu_page(
133 'MxChat Settings',
134 'MxChat',
135 'manage_options',
136 'mxchat-max',
137 array($this, 'mxchat_create_admin_page'),
138 'dashicons-testimonial',
139 6
140 );
141
142 // Submenu page for Knowledge
143 add_submenu_page(
144 'mxchat-max',
145 'Prompts',
146 'Knowledge',
147 'manage_options',
148 'mxchat-prompts',
149 array($this, 'mxchat_create_prompts_page')
150 );
151
152 // Submenu page for Chat Transcripts
153 add_submenu_page(
154 'mxchat-max', // Corrected parent slug to match the main menu
155 'Chat Transcripts',
156 'Transcripts',
157 'manage_options',
158 'mxchat-transcripts',
159 array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
160 );
161
162 // Submenu page for Intents
163 add_submenu_page(
164 'mxchat-max', // Parent slug
165 'MxChat Intents', // Page title
166 'Intents', // Menu title
167 'manage_options', // Capability
168 'mxchat-intents', // Menu slug
169 array($this, 'mxchat_intents_page_html') // Callback function
170 );
171
172 // Submenu page for Activation Key
173 add_submenu_page(
174 'mxchat-max',
175 'Pro Upgrade',
176 'Pro Upgrade',
177 'manage_options',
178 'mxchat-activation',
179 array($this, 'mxchat_create_activation_page')
180 );
181
182
183 }
184
185
186 public function mxchat_handle_content_submission() {
187 // Check if the form was submitted and the user has sufficient permissions
188 if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
189 return;
190 }
191
192 // Verify the nonce field for security
193 $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
194 if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
195 wp_die('Nonce verification failed.');
196 }
197
198 // Sanitize the content input
199 $article_content = sanitize_textarea_field($_POST['article_content']);
200
201 // Sanitize the URL input (optional URL)
202 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : ''; // Default to empty if not provided
203
204 // Generate the embedding vector for the content
205 $embedding_vector = $this->mxchat_generate_embedding($article_content);
206
207 global $wpdb;
208 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
209
210 // Check if the 'source_url' column exists in the table and add it if it doesn't
211 if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
212 // Use wpdb::query and a prepared statement to avoid SQL injection
213 $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
214 }
215
216 if (is_array($embedding_vector)) {
217 // Serialize the embedding vector before storing it
218 $embedding_vector_serialized = serialize($embedding_vector);
219
220 // Insert the content, embedding vector, and source URL into the database, using a prepared statement
221 $inserted = $wpdb->insert(
222 $table_name,
223 array(
224 'article_content' => $article_content,
225 'embedding_vector' => $embedding_vector_serialized,
226 'source_url' => $article_url, // Insert the URL, empty string if not provided
227 ),
228 array(
229 '%s', // Format for article_content (string)
230 '%s', // Format for embedding_vector (serialized string)
231 '%s', // Format for source_url (string)
232 )
233 );
234
235 if ($inserted === false) {
236 //error_log('Error inserting content: ' . $wpdb->last_error);
237 set_transient('mxchat_admin_notice_error', 'Error inserting content into the database. Please try again.', 30);
238 } else {
239 set_transient('mxchat_admin_notice_success', 'Content successfully submitted!', 30);
240 }
241
242 } else {
243 //error_log('Embedding generation failed for article content: ' . $article_content);
244 set_transient('mxchat_admin_notice_error', 'Embedding generation failed. Please ensure your API key is correct and try again.', 30);
245 }
246
247 // Redirect after setting the transient
248 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
249 exit;
250 }
251
252 public function mxchat_display_admin_notice() {
253 // Success notice
254 if ($message = get_transient('mxchat_admin_notice_success')) {
255 ?>
256 <div class="notice notice-success is-dismissible">
257 <p><?php echo esc_html($message); ?></p>
258 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
259 </div>
260 <?php
261 delete_transient('mxchat_admin_notice_success'); // Clear the transient after displaying
262 }
263
264 // Error notice
265 if ($message = get_transient('mxchat_admin_notice_error')) {
266 ?>
267 <div class="notice notice-error is-dismissible">
268 <p><?php echo esc_html($message); ?></p>
269 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
270 </div>
271 <?php
272 delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
273 }
274 }
275
276
277
278
279
280 public function mxchat_create_admin_page() {
281 ?>
282 <div class="wrap mxchat-admin">
283 <?php if (!$this->is_activated): ?>
284 <div class="mxchat-pro-banner">
285 <p>
286 Lock in a lifetime MxChat Pro license for $49.97 before 01.01.25! Afterward, it’s $99.97/year. Secure the lifetime deal now!
287 <a href="https://mxchat.ai/" target="_blank">Upgrade to Pro today!</a>
288 </p>
289
290 </div>
291
292 <?php endif; ?>
293
294 <div class="mxchat-agents-banner">
295 <p>
296 Exciting Update: For a limited time, Pro users now get access to <a href="https://mxchat.ai/agents/" target="_blank">MxChat AI Agents</a> a revolutionary way to test and deploy your MxChat chatbot!
297 </p>
298
299 </div>
300 <h2 class="admin-title">Mx<span class="admin-emphasis">Chat</span></h2>
301 <h2 class="mxchat-nav-tab-wrapper">
302 <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot">Chatbot</a>
303 <a href="#embed" class="mxchat-nav-tab" data-tab="embed">Integrations</a>
304 <a href="#theme" class="mxchat-nav-tab" data-tab="theme">Theme</a>
305 <a href="#general" class="mxchat-nav-tab" data-tab="general">FAQ</a>
306 </h2>
307 <form method="post" action="options.php">
308 <?php settings_fields('mxchat_option_group'); ?>
309 <div id="chatbot" class="mxchat-tab-content active">
310 <?php do_settings_sections('mxchat-chatbot'); ?>
311 </div>
312
313
314
315 <div id="embed" class="mxchat-tab-content">
316 <div class="mxchat-settings-section">
317 <h2>WooCommerce Settings</h2>
318 <table class="form-table">
319 <?php do_settings_fields('mxchat-embed', 'mxchat_woocommerce_section'); ?>
320 </table>
321 </div>
322
323 <div class="section-divider"></div> <!-- Divider -->
324
325 <div class="mxchat-settings-section">
326 <h2>Loops Settings</h2>
327 <table class="form-table">
328 <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
329 </table>
330 </div>
331
332
333 <div class="section-divider"></div> <!-- Divider -->
334
335 <!-- Brave Search Settings Section -->
336 <div class="mxchat-settings-section">
337 <h2>Brave Search Settings</h2>
338 <table class="form-table">
339 <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
340 </table>
341 </div>
342
343 <div class="section-divider"></div> <!-- Divider -->
344
345 <!-- Chat with PDF Intent Settings Section -->
346 <div class="mxchat-settings-section">
347 <h2>Toolbar Settings & Intents</h2>
348 <table class="form-table">
349 <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
350 </table>
351 </div>
352
353 <div class="section-divider"></div> <!-- Divider -->
354 <!-- Live Agent Settings Section -->
355 <div class="mxchat-settings-section">
356 <h2>Live Agent Settings</h2>
357 <p>Visit our <a href="https://mxchat.ai/documentation/#slack_integration" target="_blank">documentation page</a> to set up live agent transfer via Slack.</p>
358 <table class="form-table">
359 <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
360 </table>
361 </div>
362
363 </div>
364
365
366
367
368
369 <div id="theme" class="mxchat-tab-content">
370 <?php do_settings_sections('mxchat-theme'); ?>
371 </div>
372 <div id="general" class="mxchat-tab-content">
373 <?php do_settings_sections('mxchat-general'); ?>
374 <p>
375 If you’re having trouble with setup or getting the responses you need, we encourage you to review our
376 <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer">documentation</a> or
377 <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer">create a support ticket</a>.
378 </p>
379 <p>
380 If you like our plugin, please consider <a href="https://wordpress.org/plugins/mxchat-basic/#reviews" target="_blank" rel="noopener noreferrer">leaving us a review</a>.
381 </p>
382
383 <div class="faq-item">
384 <h3>How does the Claude API integration work?</h3>
385 <p>
386 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.
387 </p>
388 <p>
389 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.
390 </p>
391 <p>
392 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>.
393 </p>
394 </div>
395
396 <div class="faq-item">
397 <h3>How does the X.AI API integration work?</h3>
398 <p>
399 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.
400 </p>
401 <p>
402 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>.
403 </p>
404 </div>
405
406 <div class="faq-item">
407 <h3>Do I need an OpenAI API key to use the chatbot?</h3>
408 <p>
409 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.
410 </p>
411 <p>
412 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.
413 </p>
414 </div>
415
416 <div class="faq-item">
417 <h3>How do I add the chatbot to my site?</h3>
418 <p>
419 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.
420 </p>
421 </div>
422
423 <div class="faq-item">
424 <h3>How does the chatbot use my content to generate responses?</h3>
425 <p>
426 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.
427 </p>
428 </div>
429
430 <div class="faq-item">
431 <h3>Why does the chatbot sometimes make up links or information?</h3>
432 <p>
433 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.
434 </p>
435 </div>
436
437 <div class="faq-item">
438 <h3>How do intents work in MxChat?</h3>
439 <p>
440 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>.
441 </p>
442 </div>
443
444
445 <div class="faq-item">
446 <h3>How does the Complianz integration work?</h3>
447 <p>
448 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.
449 </p>
450 </div>
451
452 <div class="faq-item">
453 <h3>How does the WooCommerce integration work?</h3>
454 <p>
455 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.
456 </p>
457 </div>
458
459 <div class="faq-item">
460 <h3>Why isn't my chatbot responding as expected?</h3>
461 <p>
462 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.
463 </p>
464 <p>
465 We’re dedicated to your success and ready to guide you in aligning the AI's behavior to meet your goals.
466 </p>
467 </div>
468
469 <div class="faq-item">
470 <h3>What is Loops, and how do I get an API key?</h3>
471 <p>
472 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.
473 </p>
474 </div>
475
476
477 </div>
478 <?php submit_button(); ?>
479 </form>
480 </div>
481 <?php
482 }
483
484
485 public function mxchat_create_transcripts_page() {
486 ?>
487 <div class="wrap mxchat-admin">
488 <h2><?php esc_html_e('Chat Transcripts', 'mxchat'); ?></h2>
489 <form id="mxchat-delete-form" method="post">
490 <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
491 <div class="mxchat-controls">
492 <label for="mxchat-select-all-transcripts" class="mxchat-select-all-label">
493 <input type="checkbox" id="mxchat-select-all-transcripts" /> <?php esc_html_e('Select All', 'mxchat'); ?>
494 </label>
495 <input type="submit" value="<?php esc_attr_e('Delete Selected', 'mxchat'); ?>" class="button delete-chats-button" />
496 </div>
497 <div id="mxchat-transcripts">
498 <!-- Transcripts will be loaded here -->
499 </div>
500 </form>
501 </div>
502 <?php
503 }
504
505
506
507
508 public function mxchat_create_prompts_page() {
509 global $wpdb;
510 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
511
512 // Display success message if all prompts were deleted
513 if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
514 echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
515 }
516
517 // Set up pagination and search query
518 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
519 $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
520 $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
521 $per_page = 10;
522 $offset = ($current_page - 1) * $per_page;
523
524 // Modify query to handle search input
525 $sql_search = "";
526 if ($search_query) {
527 $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
528 }
529
530 // Retrieve total number of prompts, considering search filter
531 $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} {$sql_search}");
532 $total_pages = ceil($total_prompts / $per_page);
533
534 // Retrieve prompts from the database
535 $prompts = $wpdb->get_results(
536 $wpdb->prepare(
537 "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
538 $per_page,
539 $offset
540 )
541 );
542
543 ?>
544
545 <div class="wrap mxchat-admin">
546 <div class="mxchat-grid-container">
547 <!-- Submit Content Form -->
548 <div class="mxchat-grid-item full-width">
549 <h2>Submit Content</h2>
550 <form id="mxchat-content-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
551 <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
552 <div class="mxchat-form-group">
553 <label for="article_content">Article Content:</label>
554 <textarea name="article_content" id="article_content" required></textarea>
555 </div>
556 <div class="mxchat-form-group">
557 <label for="article_url">Article URL (Optional):</label>
558 <input type="url" name="article_url" id="article_url" placeholder="Enter related URL for the content">
559 </div>
560 <input type="submit" name="submit_content" value="Submit Content" class="button button-primary submit-content-button" />
561 </form>
562 <div id="mxchat-content-loading" class="mxchat-content-spinner" style="display: none;"></div>
563 <div id="mxchat-content-loading-text" class="mxchat-loading-text">Submitting content, please wait...</div>
564 </div>
565
566
567 <div class="mxchat-grid-item">
568 <h2><?php esc_html_e('Submit Sitemap, URL, or PDF URL', 'mxchat'); ?></h2>
569
570 <?php
571 // Check for any active processing
572 $pdf_url = get_transient('mxchat_last_pdf_url');
573 $sitemap_url = get_transient('mxchat_last_sitemap_url');
574 $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false;
575 $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false;
576
577 // Clear old completed statuses
578 if ($pdf_status && $pdf_status['status'] === 'complete') {
579 delete_transient('mxchat_last_pdf_url');
580 $pdf_status = false;
581 }
582 if ($sitemap_status && $sitemap_status['status'] === 'complete') {
583 delete_transient('mxchat_last_sitemap_url');
584 $sitemap_status = false;
585 }
586
587 $is_processing = ($pdf_status && $pdf_status['status'] === 'processing') ||
588 ($sitemap_status && $sitemap_status['status'] === 'processing');
589 ?>
590
591 <?php if (!$is_processing) : ?>
592 <form id="mxchat-sitemap-form" method="post" class="mxchat-sitemap-form"
593 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
594
595 <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
596
597 <div class="mxchat-search-group">
598 <input type="url"
599 name="sitemap_url"
600 id="sitemap_url"
601 placeholder="<?php esc_attr_e('Enter URL (Sitemap, PDF, or webpage)', 'mxchat'); ?>"
602 required
603 aria-label="<?php esc_attr_e('URL Input', 'mxchat'); ?>"
604 />
605 <input type="submit"
606 name="submit_sitemap"
607 value="<?php esc_attr_e('Submit', 'mxchat'); ?>"
608 class="button button-primary"
609 />
610 </div>
611 </form>
612 <?php endif; ?>
613
614 <div id="mxchat-sitemap-loading" class="mxchat-spinner" style="display: none;"
615 aria-hidden="true"></div>
616 <div id="mxchat-loading-text" class="screen-reader-text" style="display: none;">
617 <?php esc_html_e('Loading information into database, please wait...', 'mxchat'); ?>
618 </div>
619
620 <?php if ($pdf_status && $pdf_status['status'] !== 'complete') : ?>
621 <div class="mxchat-process-status pdf" role="alert" aria-live="polite">
622 <h3><?php esc_html_e('PDF Processing Status (Refresh page for update)', 'mxchat'); ?></h3>
623 <p>
624 <?php
625 printf(
626 /* translators: 1: Current pages, 2: Total pages, 3: Percentage complete */
627 esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
628 absint($pdf_status['processed_pages']),
629 absint($pdf_status['total_pages']),
630 absint($pdf_status['percentage'])
631 );
632 ?>
633 </p>
634 <p>
635 <?php
636 printf(
637 /* translators: %s: Current status */
638 esc_html__('Status: %s', 'mxchat'),
639 esc_html(ucfirst($pdf_status['status']))
640 );
641 ?>
642 </p>
643 <p>
644 <?php
645 printf(
646 /* translators: %s: Time since last update */
647 esc_html__('Last update: %s', 'mxchat'),
648 esc_html($pdf_status['last_update'])
649 );
650 ?>
651 </p>
652 </div>
653 <?php endif; ?>
654
655 <?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
656 <div class="mxchat-process-status sitemap" role="alert" aria-live="polite">
657 <h3><?php esc_html_e('Sitemap Processing Status (Refresh page for update)', 'mxchat'); ?></h3>
658 <p>
659 <?php
660 printf(
661 /* translators: 1: Processed URLs, 2: Total URLs, 3: Percentage complete */
662 esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
663 absint($sitemap_status['processed_urls']),
664 absint($sitemap_status['total_urls']),
665 absint($sitemap_status['percentage'])
666 );
667 ?>
668 </p>
669 <p>
670 <?php
671 printf(
672 /* translators: %s: Current status */
673 esc_html__('Status: %s', 'mxchat'),
674 esc_html(ucfirst($sitemap_status['status']))
675 );
676 ?>
677 </p>
678 <p>
679 <?php
680 printf(
681 /* translators: %s: Time since last update */
682 esc_html__('Last update: %s', 'mxchat'),
683 esc_html($sitemap_status['last_update'])
684 );
685 ?>
686 </p>
687 </div>
688 <?php endif; ?>
689
690 <?php if ($is_processing) : ?>
691 <form id="mxchat-stop-form" method="post" class="mxchat-stop-form"
692 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
693
694 <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
695
696 <input type="submit"
697 name="stop_processing"
698 value="<?php esc_attr_e('Stop Processing', 'mxchat'); ?>"
699 class="button button-secondary"
700 />
701 </form>
702 <?php endif; ?>
703
704 </div>
705
706 <!-- Search Knowledge Form -->
707 <div class="mxchat-grid-item">
708 <h2>Search Knowledge</h2>
709 <form method="get" id="knowledge-search">
710 <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
711 <input type="hidden" name="page" value="mxchat-prompts" />
712 <div class="mxchat-search-group">
713 <input type="text" name="search" placeholder="Search Knowledge" value="<?php echo esc_attr($search_query); ?>" />
714 <input type="submit" value="Search" class="button button-primary" />
715 </div>
716 </form>
717 </div>
718 </div>
719
720 <!-- Table navigation with Delete All Button -->
721 <div class="tablenav">
722 <div class="tablenav-pages">
723 <span class="displaying-num"><?php echo esc_html($total_prompts); ?> items</span>
724
725 <!-- Delete All Prompts Button -->
726 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>" style="display: inline;" onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all prompts? This action cannot be undone.', 'mxchat'); ?>');">
727 <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
728 <input type="submit" name="delete_all_prompts" value="<?php esc_attr_e('Delete All Knowledge', 'mxchat'); ?>" class="button mxchat-delete-all" />
729 </form>
730
731 <?php
732 // Display pagination links
733 $page_links = paginate_links(array(
734 'base' => add_query_arg(array('paged' => '%#%', 'search' => urlencode($search_query), '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')), admin_url('admin.php?page=mxchat-prompts')),
735 'format' => '',
736 'prev_text' => __('&laquo; Previous'),
737 'next_text' => __('Next &raquo;'),
738 'total' => $total_pages,
739 'current' => $current_page,
740 ));
741
742 if ($page_links) {
743 echo '<div class="tablenav-pages">' . wp_kses_post($page_links) . '</div>';
744 }
745 ?>
746 </div>
747 </div>
748
749 <!-- Prompts Table -->
750 <table class="mxchat-table">
751 <thead>
752 <tr>
753 <th>ID</th>
754 <th>Article Content</th>
755 <th>URL</th>
756 <th>Actions</th>
757 </tr>
758 </thead>
759 <tbody>
760 <?php if ($prompts) : ?>
761 <?php foreach ($prompts as $prompt) : ?>
762 <tr id="prompt-<?php echo esc_attr($prompt->id); ?>">
763 <td><?php echo esc_html($prompt->id); ?></td>
764 <td class="mxchat_article_content_dashboard">
765 <span class="content-view"><?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?></span>
766 <textarea class="content-edit" style="display:none;"><?php echo esc_textarea($prompt->article_content); ?></textarea>
767 </td>
768 <td class="mxchat_article_url_dashboard">
769 <span class="url-view">
770 <?php if (!empty($prompt->source_url)) : ?>
771 <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank"><?php echo esc_html($prompt->source_url); ?></a>
772 <?php else : ?>
773 N/A
774 <?php endif; ?>
775 </span>
776 <input type="url" class="url-edit" value="<?php echo esc_attr($prompt->source_url); ?>" style="display:none;" />
777 </td>
778 <td>
779 <button class="button edit-button" data-id="<?php echo esc_attr($prompt->id); ?>">Edit</button>
780 <button class="button save-button" data-id="<?php echo esc_attr($prompt->id); ?>" style="display:none;">Save</button>
781 <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>
782 </td>
783 </tr>
784 <?php endforeach; ?>
785 <?php else : ?>
786 <tr>
787 <td colspan="4"><?php esc_html_e('No prompts found.', 'mxchat'); ?></td>
788 </tr>
789 <?php endif; ?>
790 </tbody>
791 </table>
792 </div>
793 <?php
794 }
795
796
797 // Delete All Prompts
798 public function mxchat_handle_delete_all_prompts() {
799 // Verify nonce
800 if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
801 wp_die(__('Nonce verification failed.', 'mxchat'));
802 }
803
804 // Check permissions
805 if (!current_user_can('manage_options')) {
806 wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
807 }
808
809 global $wpdb;
810 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
811
812 // Delete all prompts from the table
813 $wpdb->query("DELETE FROM {$table_name}");
814
815 // Clear relevant cache
816 wp_cache_delete('all_prompts', 'mxchat_prompts');
817
818 // Redirect back with a success message
819 $redirect_url = add_query_arg(array(
820 'page' => 'mxchat-prompts',
821 'all_deleted' => 'true'
822 ), admin_url('admin.php'));
823
824 wp_safe_redirect($redirect_url);
825 exit;
826 }
827
828 // Single Prompt Deletion
829 public function mxchat_handle_delete_prompt() {
830 // Sanitize and validate nonce
831 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
832 if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
833 wp_die('Nonce verification failed.');
834 }
835
836 // Check permissions
837 if (!current_user_can('manage_options')) {
838 wp_die('You do not have sufficient permissions to delete prompts.');
839 }
840
841 // Validate and sanitize ID parameter
842 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
843 if ($id <= 0) {
844 wp_die('Invalid prompt ID.');
845 }
846
847 global $wpdb;
848 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
849
850 // Clear cache and delete prompt
851 wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
852 $wpdb->delete($table_name, array('id' => $id), array('%d'));
853
854 wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
855 exit;
856 }
857
858
859
860
861 public function mxchat_generate_embedding($text) {
862 $options = get_option('mxchat_options');
863 $api_key = $options['api_key'] ?? 'default_api_key';
864
865 $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
866 'body' => wp_json_encode(array(
867 'model' => 'text-embedding-ada-002',
868 'input' => $text
869 )),
870 'headers' => array(
871 'Authorization' => 'Bearer ' . $api_key,
872 'Content-Type' => 'application/json'
873 ),
874 ));
875
876 if (is_wp_error($response)) {
877 return null;
878 }
879
880 $response_data = json_decode(wp_remote_retrieve_body($response), true);
881 return $response_data['data'][0]['embedding'] ?? null;
882 }
883
884 public function mxchat_delete_chat_history() {
885 if (!current_user_can('manage_options')) {
886 echo wp_json_encode(['error' => 'You do not have sufficient permissions.']);
887 wp_die();
888 }
889
890 check_ajax_referer('mxchat_delete_chat_history', 'security');
891
892 global $wpdb;
893 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
894
895 if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
896 foreach ($_POST['delete_session_ids'] as $session_id) {
897 $session_id_sanitized = sanitize_text_field($session_id);
898
899 // Clear relevant cache before deletion
900 $cache_key = 'chat_session_' . $session_id_sanitized;
901 wp_cache_delete($cache_key, 'mxchat_chat_sessions');
902
903 // Perform the deletion
904 $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
905 }
906
907 // Optionally, clear a general cache if you have one
908 wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
909
910 echo wp_json_encode(['success' => 'Selected chat sessions have been deleted.']);
911 } else {
912 echo wp_json_encode(['error' => 'No chat sessions selected for deletion.']);
913 }
914
915 wp_die();
916 }
917
918
919 public function mxchat_save_inline_prompt() {
920 // Check for nonce security
921 check_ajax_referer('mxchat_save_inline_nonce');
922
923 // Verify permissions
924 if (!current_user_can('manage_options')) {
925 wp_send_json_error('Permission denied.');
926 return;
927 }
928
929 global $wpdb;
930 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
931
932 // Validate and sanitize input data
933 $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
934 $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : '';
935 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
936
937 if ($prompt_id > 0 && !empty($article_content)) {
938 // Re-generate the embedding vector for the updated content
939 $embedding_vector = $this->mxchat_generate_embedding($article_content);
940
941 if (is_array($embedding_vector)) {
942 // Serialize the embedding vector before storing it
943 $embedding_vector_serialized = serialize($embedding_vector);
944
945 // Update the prompt in the database
946 $updated = $wpdb->update(
947 $table_name,
948 array(
949 'article_content' => $article_content,
950 'embedding_vector' => $embedding_vector_serialized,
951 'source_url' => $article_url,
952 ),
953 array('id' => $prompt_id),
954 array('%s', '%s', '%s'),
955 array('%d')
956 );
957
958 if ($updated !== false) {
959 wp_send_json_success();
960 } else {
961 wp_send_json_error('Database update failed.');
962 }
963 } else {
964 wp_send_json_error('Embedding generation failed.');
965 }
966 } else {
967 wp_send_json_error('Invalid data.');
968 }
969 }
970
971 public function mxchat_fetch_chat_history() {
972 global $wpdb;
973 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
974
975 if (!current_user_can('manage_options')) {
976 wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
977 }
978
979 // First get unique session IDs ordered by most recent message in each session
980 $session_ids = $wpdb->get_col(
981 $wpdb->prepare(
982 "SELECT DISTINCT session_id
983 FROM {$table_name}
984 GROUP BY session_id
985 ORDER BY MAX(timestamp) DESC"
986 )
987 );
988
989 if (empty($session_ids)) {
990 wp_die(esc_html__('No chat history available.', 'mxchat'));
991 }
992
993 ob_start();
994 echo '<div class="mxchat-transcript">';
995
996 // Iterate through sessions from newest to oldest
997 foreach ($session_ids as $session_id) {
998 // Get messages for this session ordered by timestamp
999 $messages = $wpdb->get_results(
1000 $wpdb->prepare(
1001 "SELECT * FROM {$table_name}
1002 WHERE session_id = %s
1003 ORDER BY timestamp ASC",
1004 $session_id
1005 )
1006 );
1007
1008 // Start session block
1009 echo '<div class="mxchat-session">';
1010 echo '<div class="mxchat-session-header">';
1011 echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($session_id) . '"> ';
1012 echo esc_html($session_id);
1013 echo '</div>';
1014 echo '<div class="mxchat-messages">';
1015
1016 // Display messages for this session
1017 foreach ($messages as $transcript) {
1018 $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
1019
1020 // Determine message styling
1021 switch ($transcript->role) {
1022 case 'assistant':
1023 case 'bot':
1024 $message_class = 'bot-message';
1025 $display_role = esc_html__('Chatbot', 'mxchat');
1026 break;
1027 case 'user':
1028 $message_class = 'user-message';
1029 $display_role = !empty($transcript->user_identifier)
1030 ? sanitize_text_field($transcript->user_identifier)
1031 : esc_html__('User', 'mxchat');
1032 break;
1033 case 'agent':
1034 $message_class = 'agent-message';
1035 $display_role = esc_html__('Agent', 'mxchat');
1036 break;
1037 default:
1038 $message_class = 'unknown-message';
1039 $display_role = esc_html__('Unknown', 'mxchat');
1040 }
1041
1042 // Process message content
1043 $message_content = wp_kses(
1044 stripslashes($transcript->message),
1045 [
1046 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1047 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1048 'a' => ['href' => [], 'title' => []]
1049 ]
1050 );
1051 $message_content = nl2br($message_content);
1052
1053 // Render message
1054 echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
1055 echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
1056 echo '<div class="mxchat-message-content">' . $message_content . '</div>';
1057 echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
1058 echo '</div>';
1059 }
1060
1061 // Close session block
1062 echo '</div></div>';
1063 }
1064
1065 echo '</div>';
1066 $output = ob_get_clean();
1067 echo $output;
1068 wp_die();
1069 }
1070
1071
1072 public function mxchat_create_activation_page() {
1073 $license_status = get_option('mxchat_license_status', 'inactive');
1074 $license_error = get_option('mxchat_license_error', '');
1075 ?>
1076 <div class="wrap mxchat-admin">
1077 <h2>MxChat Pro: Activation</h2>
1078 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
1079 <div class="error notice">
1080 <p><?php echo esc_html($license_error); ?></p>
1081 </div>
1082 <?php endif; ?>
1083
1084 <form id="mxchat-activation-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
1085 <table class="form-table">
1086 <tr valign="top">
1087 <th scope="row">Email Address</th>
1088 <td>
1089 <input type="email" id="mxchat_pro_email" name="mxchat_pro_email" value="<?php echo esc_attr(get_option('mxchat_pro_email')); ?>" class="regular-text" required />
1090 </td>
1091 </tr>
1092 <tr valign="top">
1093 <th scope="row">Activation Key</th>
1094 <td>
1095 <input type="text" id="mxchat_activation_key" name="mxchat_activation_key" value="<?php echo esc_attr(get_option('mxchat_activation_key')); ?>" class="regular-text" required />
1096 </td>
1097 </tr>
1098 </table>
1099 <?php if ($license_status !== 'active'): ?>
1100 <button type="submit" id="activate_license_button" class="button button-primary"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
1101 <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
1102 <?php endif; ?>
1103 </form>
1104
1105 <!-- License Status Display -->
1106 <h3>License Status: <span id="mxchat-license-status"><?php echo $license_status === 'active' ? 'Active' : 'Inactive'; ?></span></h3>
1107 </div>
1108 <?php
1109 }
1110
1111 public function mxchat_intents_page_html() {
1112 if ( ! current_user_can( 'manage_options' ) ) {
1113 return;
1114 }
1115
1116 // Fetch existing intents with pagination and filtering
1117 global $wpdb;
1118 $table_name = $wpdb->prefix . 'mxchat_intents';
1119 $page = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
1120 $per_page = 20;
1121 $offset = ( $page - 1 ) * $per_page;
1122
1123
1124 // Add at the start of mxchat_intents_page_html()
1125 if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
1126 echo '<div class="notice notice-success is-dismissible"><p>' .
1127 esc_html__('Intent updated successfully.', 'mxchat') .
1128 '</p></div>';
1129 }
1130
1131 // Build the WHERE clause based on filters
1132 $where = '1=1';
1133 $search_term = isset( $_GET['s'] ) ? trim( $_GET['s'] ) : '';
1134 $callback_filter = isset( $_GET['callback_filter'] ) ? sanitize_text_field( $_GET['callback_filter'] ) : '';
1135
1136 if ( $search_term ) {
1137 $search_term_like = '%' . $wpdb->esc_like( $search_term ) . '%';
1138 $where .= $wpdb->prepare( ' AND (intent_label LIKE %s OR phrases LIKE %s)', $search_term_like, $search_term_like );
1139 }
1140
1141 if ( $callback_filter ) {
1142 $where .= $wpdb->prepare( ' AND callback_function = %s', $callback_filter );
1143 }
1144
1145 // Get total count for pagination
1146 $total_intents = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE $where" );
1147 $total_pages = ceil( $total_intents / $per_page );
1148
1149 // Fetch intents with pagination and filters
1150 $intents = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d", $per_page, $offset ) );
1151
1152 // Get available callback functions with 'pro_only' flag
1153 $available_callbacks = $this->mxchat_get_available_callbacks();
1154 ?>
1155
1156 <div class="wrap mxchat-admin">
1157 <!-- Add Intent Form -->
1158 <form id="mxchat-add-intent-form" method="post" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>">
1159 <input type="hidden" name="action" value="mxchat_add_intent">
1160 <?php wp_nonce_field( 'mxchat_add_intent_nonce' ); ?>
1161 <h2><?php esc_html_e( 'Add New Intent', 'mxchat' ); ?></h2>
1162 <p class="description">
1163 <?php esc_html_e( 'We highly encourage users to quickly read our ', 'mxchat' ); ?>
1164 <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
1165 <?php esc_html_e( 'documentation', 'mxchat' ); ?>
1166 </a>
1167 <?php esc_html_e( ' to better understand intents. Some intents require setup in the Integration tab. If your intent is not triggering lower similarity threshold test.', 'mxchat' ); ?>
1168 </p>
1169
1170 <div class="mxchat-form-group">
1171 <label for="intent_label"><?php esc_html_e( 'Intent Label (For your reference only)', 'mxchat' ); ?></label>
1172 <input name="intent_label" type="text" id="intent_label" class="regular-text" required
1173 placeholder="Example Email Capture: Newsletter Signup">
1174 </div>
1175 <div class="mxchat-form-group">
1176 <label for="phrases"><?php esc_html_e( 'Phrases (comma-separated)', 'mxchat' ); ?></label>
1177 <textarea name="phrases" id="phrases" rows="5" class="large-text" required
1178 placeholder="Example Email Capture: sign me up, subscribe me, I want to join, add me to the newsletter, send me updates, keep me informed"></textarea>
1179 </div>
1180 <div class="mxchat-form-group">
1181 <label for="callback_function"><?php esc_html_e( 'Callback Function', 'mxchat' ); ?></label>
1182 <select name="callback_function" id="callback_function" required>
1183 <option value=""><?php esc_html_e('Select a Callback', 'mxchat'); ?></option>
1184 <?php
1185 $groups = $this->mxchat_get_available_callbacks(true); // Fetch grouped data
1186
1187 foreach ($groups as $group_label => $group_callbacks) :
1188 echo '<optgroup label="' . esc_attr($group_label) . '">';
1189 foreach ($group_callbacks as $function => $data) :
1190 $label = $data['label'];
1191 $pro_only = $data['pro_only'];
1192 $disabled = (!$this->is_activated && $pro_only) ? 'disabled' : '';
1193 $label_suffix = (!$this->is_activated && $pro_only) ? ' (Pro Only)' : '';
1194 ?>
1195 <option value="<?php echo esc_attr($function); ?>" <?php echo $disabled; ?>>
1196 <?php echo esc_html($label . $label_suffix); ?>
1197 </option>
1198 <?php endforeach;
1199 echo '</optgroup>';
1200 endforeach;
1201 ?>
1202 </select>
1203
1204
1205
1206 </div>
1207 <button type="submit" class="button button-primary submit-content-button">
1208 <?php esc_html_e( 'Add Intent', 'mxchat' ); ?>
1209 </button>
1210 <div id="mxchat-intent-loading" style="display: none;"></div>
1211 <div id="mxchat-intent-loading-text" style="display: none;">
1212 <?php esc_html_e( 'Saving intent, please wait...', 'mxchat' ); ?>
1213 </div>
1214
1215 </form>
1216
1217 <!-- Filter Form -->
1218 <form method="get" action="">
1219 <input type="hidden" name="page" value="mxchat-intents">
1220 <div class="mxchat-search-group">
1221 <input type="text" name="s" id="mxchat-intent-search" placeholder="<?php esc_attr_e( 'Search Intents', 'mxchat' ); ?>" value="<?php echo esc_attr( $search_term ); ?>">
1222 <select name="callback_filter" id="mxchat-callback-filter">
1223 <option value=""><?php esc_html_e( 'All Callbacks', 'mxchat' ); ?></option>
1224 <?php foreach ( $available_callbacks as $function => $callback_data ) : ?>
1225 <?php $label = $callback_data['label']; ?>
1226 <option value="<?php echo esc_attr( $function ); ?>" <?php selected( $callback_filter, $function ); ?>><?php echo esc_html( $label ); ?></option>
1227 <?php endforeach; ?>
1228 </select>
1229 <button type="submit" class="button"><?php esc_html_e( 'Filter', 'mxchat' ); ?></button>
1230 </div>
1231 </form>
1232
1233 <!-- Intents Table -->
1234 <table class="wp-list-table mxchat-intents-table widefat fixed striped">
1235 <thead>
1236 <tr>
1237 <th><?php esc_html_e( 'Intent Label', 'mxchat' ); ?></th>
1238 <th><?php esc_html_e( 'Phrases', 'mxchat' ); ?></th>
1239 <th><?php esc_html_e( 'Callback Function', 'mxchat' ); ?></th>
1240 <th><?php esc_html_e( 'Similarity Threshold', 'mxchat' ); ?></th>
1241 <th><?php esc_html_e( 'Actions', 'mxchat' ); ?></th>
1242 </tr>
1243 </thead>
1244 <tbody>
1245 <?php if ( $intents ) : ?>
1246 <?php foreach ( $intents as $intent ) : ?>
1247 <?php
1248 $callback_function = $intent->callback_function;
1249 $callback_label = isset( $available_callbacks[ $callback_function ]['label'] ) ? $available_callbacks[ $callback_function ]['label'] : $callback_function;
1250 $threshold_value = isset( $intent->similarity_threshold ) ? round( $intent->similarity_threshold * 100 ) : 85;
1251 ?>
1252 <tr>
1253 <td><?php echo esc_html( $intent->intent_label ); ?></td>
1254 <td><?php echo esc_html( $intent->phrases ); ?></td>
1255 <td><?php echo esc_html( $callback_label ); ?></td>
1256 <!-- Similarity Threshold Column -->
1257 <td>
1258 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
1259 <?php wp_nonce_field( 'mxchat_update_intent_threshold_nonce' ); ?>
1260 <input type="hidden" name="action" value="mxchat_update_intent_threshold">
1261 <input type="hidden" name="intent_id" value="<?php echo esc_attr( $intent->id ); ?>">
1262 <input type="range" name="intent_threshold" id="intent_threshold_<?php echo esc_attr( $intent->id ); ?>" min="70" max="95" value="<?php echo esc_attr( $threshold_value ); ?>" oninput="document.getElementById('threshold_output_<?php echo esc_attr( $intent->id ); ?>').value = this.value + '%'">
1263 <output id="threshold_output_<?php echo esc_attr( $intent->id ); ?>"><?php echo esc_html( $threshold_value ); ?>%</output>
1264 </td>
1265 <!-- Actions Column -->
1266 <td>
1267 <button type="submit" class="button button-primary mxchat-save-button">
1268 <?php esc_html_e( 'Save', 'mxchat' ); ?>
1269 </button>
1270 </form>
1271
1272
1273
1274
1275 <button type="button"
1276 class="button button-secondary mxchat-edit-button"
1277 data-intent-id="<?php echo esc_attr($intent->id); ?>"
1278 data-phrases="<?php echo esc_attr($intent->phrases); ?>">
1279 <?php esc_html_e('Edit', 'mxchat'); ?>
1280 </button>
1281
1282
1283
1284
1285 <!-- Delete Form -->
1286 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" onsubmit="return confirm('<?php esc_attr_e( 'Are you sure you want to delete this intent?', 'mxchat' ); ?>');">
1287 <?php wp_nonce_field( 'mxchat_delete_intent_nonce' ); ?>
1288 <input type="hidden" name="action" value="mxchat_delete_intent">
1289 <input type="hidden" name="intent_id" value="<?php echo esc_attr( $intent->id ); ?>">
1290 <button type="submit" class="button mxchat-delete-all">
1291 <?php esc_html_e( 'Delete', 'mxchat' ); ?>
1292 </button>
1293 </form>
1294 </td>
1295 </tr>
1296 <?php endforeach; ?>
1297 <?php else : ?>
1298 <tr>
1299 <td colspan="5"><?php esc_html_e( 'No intents found.', 'mxchat' ); ?></td>
1300 </tr>
1301 <?php endif; ?>
1302 </tbody>
1303 </table>
1304
1305
1306 <div id="mxchat-edit-modal" class="mxchat-modal" style="display: none;">
1307 <div class="mxchat-modal-content">
1308 <span class="mxchat-modal-close">&times;</span>
1309 <h2><?php esc_html_e('Edit Intent Phrases', 'mxchat'); ?></h2>
1310 <form id="mxchat-edit-intent-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
1311 <?php wp_nonce_field('mxchat_edit_intent_nonce'); ?>
1312 <input type="hidden" name="action" value="mxchat_edit_intent">
1313 <input type="hidden" name="intent_id" id="edit_intent_id">
1314 <div class="mxchat-form-group">
1315 <label for="edit_phrases"><?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?></label>
1316 <textarea name="phrases" id="edit_phrases" rows="5" class="large-text" required></textarea>
1317 </div>
1318 <button type="submit" class="button button-primary">
1319 <?php esc_html_e('Update Phrases', 'mxchat'); ?>
1320 </button>
1321 </form>
1322 </div>
1323 </div>
1324
1325
1326
1327
1328 </div>
1329 <?php
1330 }
1331
1332
1333
1334 /**
1335 * Handle editing of intent phrases
1336 *
1337 * @since 1.0.0
1338 * @return void
1339 */
1340 public function handle_edit_intent() {
1341 // Verify nonce and user capabilities
1342 if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'mxchat_edit_intent_nonce')) {
1343 wp_die(esc_html__('Security check failed.', 'mxchat'));
1344 }
1345
1346 if (!current_user_can('manage_options')) {
1347 wp_die(esc_html__('You do not have permission to perform this action.', 'mxchat'));
1348 }
1349
1350 // Validate and sanitize input
1351 $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
1352 if (!$intent_id) {
1353 wp_die(esc_html__('Invalid intent ID.', 'mxchat'));
1354 }
1355
1356 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
1357 if (empty($phrases_input)) {
1358 wp_die(esc_html__('Phrases cannot be empty.', 'mxchat'));
1359 }
1360
1361 // Process phrases the same way as in intent creation
1362 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
1363
1364 if (empty($phrases_array)) {
1365 wp_die(esc_html__('Please enter at least one valid phrase.', 'mxchat'));
1366 }
1367
1368 // Generate embeddings and combine them
1369 $vectors = [];
1370 foreach ($phrases_array as $phrase) {
1371 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
1372 if (is_array($embedding_vector)) {
1373 $vectors[] = $embedding_vector;
1374 } else {
1375 wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase));
1376 }
1377 }
1378
1379 if (empty($vectors)) {
1380 wp_die(esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
1381 }
1382
1383 // Create combined vector just like in intent creation
1384 $combined_vector = $this->mxchat_average_vectors($vectors);
1385 $serialized_vector = maybe_serialize($combined_vector);
1386
1387 global $wpdb;
1388 $table_name = $wpdb->prefix . 'mxchat_intents';
1389
1390 // Update the intent with both new phrases and the combined vector
1391 $result = $wpdb->update(
1392 $table_name,
1393 array(
1394 'phrases' => implode(', ', $phrases_array),
1395 'embedding_vector' => $serialized_vector
1396 ),
1397 array('id' => $intent_id),
1398 array('%s', '%s'),
1399 array('%d')
1400 );
1401
1402 if (false === $result) {
1403 wp_die(esc_html__('Failed to update intent.', 'mxchat'));
1404 }
1405
1406 // Redirect back to the intents page with a success message
1407 $redirect_url = add_query_arg(
1408 array(
1409 'page' => 'mxchat-intents',
1410 'updated' => 'true'
1411 ),
1412 admin_url('admin.php')
1413 );
1414
1415 wp_safe_redirect($redirect_url);
1416 exit;
1417 }
1418 public function mxchat_handle_update_intent_threshold() {
1419 if ( ! current_user_can( 'manage_options' ) ) {
1420 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1421 }
1422
1423 check_admin_referer('mxchat_update_intent_threshold_nonce');
1424
1425 if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
1426 global $wpdb;
1427 $table_name = $wpdb->prefix . 'mxchat_intents';
1428 $intent_id = intval($_POST['intent_id']);
1429 $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
1430 $similarity_threshold = $threshold_percentage / 100;
1431
1432 $wpdb->update(
1433 $table_name,
1434 ['similarity_threshold' => $similarity_threshold],
1435 ['id' => $intent_id],
1436 ['%f'],
1437 ['%d']
1438 );
1439 }
1440
1441 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1442 exit;
1443 }
1444
1445 public function mxchat_handle_add_intent() {
1446 if ( ! current_user_can( 'manage_options' ) ) {
1447 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1448 }
1449
1450 check_admin_referer('mxchat_add_intent_nonce');
1451
1452 global $wpdb;
1453 $table_name = $wpdb->prefix . 'mxchat_intents';
1454
1455 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
1456 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
1457 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
1458 $default_threshold = 0.85;
1459
1460 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
1461 wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') );
1462 }
1463
1464 $available_callbacks = $this->mxchat_get_available_callbacks();
1465
1466 if (!array_key_exists($callback_function, $available_callbacks)) {
1467 wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
1468 }
1469
1470 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
1471 if ($is_pro_only && !$this->is_activated) {
1472 wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
1473 }
1474
1475 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
1476
1477 if (empty($phrases_array)) {
1478 wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
1479 }
1480
1481 $vectors = [];
1482 foreach ($phrases_array as $phrase) {
1483 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
1484 if (is_array($embedding_vector)) {
1485 $vectors[] = $embedding_vector;
1486 } else {
1487 wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
1488 }
1489 }
1490
1491 if (!empty($vectors)) {
1492 $combined_vector = $this->mxchat_average_vectors($vectors);
1493 $serialized_vector = maybe_serialize($combined_vector);
1494
1495 $result = $wpdb->insert($table_name, [
1496 'intent_label' => $intent_label,
1497 'phrases' => implode(', ', $phrases_array),
1498 'embedding_vector' => $serialized_vector,
1499 'callback_function' => $callback_function,
1500 'similarity_threshold' => $default_threshold,
1501 ]);
1502
1503 if ($result === false) {
1504 wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
1505 }
1506 } else {
1507 wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
1508 }
1509
1510 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1511 exit;
1512 }
1513
1514
1515
1516
1517
1518
1519 private function mxchat_get_available_callbacks($grouped = false) {
1520 $callbacks = [
1521 'mxchat_handle_email_capture' => [
1522 'label' => 'Email Capture',
1523 'pro_only' => false,
1524 'group' => 'Customer Engagement',
1525 ],
1526 'mxchat_handle_product_inquiry' => [
1527 'label' => 'Show Product Card',
1528 'pro_only' => true,
1529 'group' => 'WooCommerce Features',
1530 ],
1531 'mxchat_handle_order_history' => [
1532 'label' => 'Order History',
1533 'pro_only' => true,
1534 'group' => 'WooCommerce Features',
1535 ],
1536 'mxchat_generate_image' => [
1537 'label' => 'Generate Image',
1538 'pro_only' => true,
1539 'group' => 'Other Features',
1540 ],
1541 'mxchat_handle_search_request' => [
1542 'label' => 'Brave Web Search',
1543 'pro_only' => false,
1544 'group' => 'Search Features',
1545 ],
1546 'mxchat_handle_image_search_request' => [
1547 'label' => 'Brave Image Search',
1548 'pro_only' => false,
1549 'group' => 'Search Features',
1550 ],
1551 'mxchat_handle_add_to_cart_intent' => [
1552 'label' => 'Add to Cart',
1553 'pro_only' => true,
1554 'group' => 'WooCommerce Features',
1555 ],
1556 'mxchat_handle_checkout_intent' => [
1557 'label' => 'Proceed to Checkout',
1558 'pro_only' => true,
1559 'group' => 'WooCommerce Features',
1560 ],
1561 'mxchat_handle_pdf_discussion' => [
1562 'label' => 'Chat with PDF',
1563 'pro_only' => true,
1564 'group' => 'Other Features',
1565 ],
1566 'mxchat_handle_product_recommendations' => [
1567 'label' => 'Product Recommendations',
1568 'pro_only' => true,
1569 'group' => 'WooCommerce Features',
1570 ],
1571 'mxchat_live_agent_handover' => [
1572 'label' => 'Live Agent',
1573 'pro_only' => true,
1574 'group' => 'Customer Engagement',
1575 ],
1576 'mxchat_handle_switch_to_chatbot_intent' => [
1577 'label' => 'Back to Chatbot',
1578 'pro_only' => true,
1579 'group' => 'Customer Engagement',
1580 ],
1581 ];
1582
1583 // Return grouped structure if requested
1584 if ($grouped) {
1585 $grouped_callbacks = [];
1586 foreach ($callbacks as $key => $data) {
1587 $group_label = $data['group'] ?? 'Other Features';
1588 $grouped_callbacks[$group_label][$key] = [
1589 'label' => $data['label'],
1590 'pro_only' => $data['pro_only'],
1591 ];
1592 }
1593 return $grouped_callbacks;
1594 }
1595
1596 return $callbacks;
1597 }
1598
1599
1600
1601 private function mxchat_average_vectors($vectors) {
1602 $vector_length = count($vectors[0]);
1603 $sum_vector = array_fill(0, $vector_length, 0);
1604
1605 foreach ($vectors as $vector) {
1606 for ($i = 0; $i < $vector_length; $i++) {
1607 $sum_vector[$i] += $vector[$i];
1608 }
1609 }
1610
1611 // Divide each component by the number of vectors to get the average
1612 $num_vectors = count($vectors);
1613 for ($i = 0; $i < $vector_length; $i++) {
1614 $sum_vector[$i] /= $num_vectors;
1615 }
1616
1617 return $sum_vector;
1618 }
1619
1620 public function mxchat_handle_delete_intent() {
1621 if ( ! current_user_can( 'manage_options' ) ) {
1622 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1623 }
1624
1625 check_admin_referer('mxchat_delete_intent_nonce');
1626
1627 if (isset($_POST['intent_id'])) {
1628 global $wpdb;
1629 $table_name = $wpdb->prefix . 'mxchat_intents';
1630 $intent_id = intval($_POST['intent_id']);
1631
1632 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
1633 }
1634
1635 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1636 exit;
1637 }
1638
1639
1640 public function mxchat_page_init() {
1641 // Register settings
1642 register_setting(
1643 'mxchat_option_group',
1644 'mxchat_options',
1645 array($this, 'mxchat_sanitize')
1646 );
1647
1648 register_setting(
1649 'mxchat_option_group',
1650 'mxchat_similarity_threshold',
1651 array(
1652 'type' => 'number',
1653 'sanitize_callback' => function($value) {
1654 $value = absint($value); // Ensure it's an integer
1655 return min(max($value, 70), 85); // Enforce range
1656 },
1657 'default' => 80,
1658 )
1659 );
1660
1661
1662 // Chatbot Settings Section
1663 add_settings_section(
1664 'mxchat_chatbot_section',
1665 'Chatbot Settings',
1666 null,
1667 'mxchat-chatbot'
1668 );
1669
1670 // Similarity Threshold Slider
1671 add_settings_field(
1672 'similarity_threshold', // Field ID
1673 'Similarity Threshold', // Field title
1674 array($this, 'mxchat_similarity_threshold_callback'), // Callback function
1675 'mxchat-chatbot', // Page
1676 'mxchat_chatbot_section' // Section
1677 );
1678
1679 // Existing fields...
1680 add_settings_field(
1681 'api_key',
1682 'OpenAI API Key',
1683 array($this, 'api_key_callback'),
1684 'mxchat-chatbot',
1685 'mxchat_chatbot_section'
1686 );
1687
1688 add_settings_field(
1689 'xai_api_key',
1690 'X.AI API Key',
1691 array($this, 'xai_api_key_callback'),
1692 'mxchat-chatbot',
1693 'mxchat_chatbot_section'
1694 );
1695
1696 add_settings_field(
1697 'claude_api_key',
1698 'Claude API Key',
1699 array($this, 'claude_api_key_callback'),
1700 'mxchat-chatbot',
1701 'mxchat_chatbot_section'
1702 );
1703
1704
1705 add_settings_field(
1706 'system_prompt_instructions',
1707 'AI Instructions (Behavior)',
1708 array($this, 'system_prompt_instructions_callback'),
1709 'mxchat-chatbot',
1710 'mxchat_chatbot_section'
1711 );
1712
1713 add_settings_field(
1714 'model',
1715 'Model',
1716 array($this, 'mxchat_model_callback'),
1717 'mxchat-chatbot',
1718 'mxchat_chatbot_section'
1719 );
1720
1721 add_settings_field(
1722 'top_bar_title',
1723 'Top Bar Title',
1724 array($this, 'mxchat_top_bar_title_callback'),
1725 'mxchat-chatbot',
1726 'mxchat_chatbot_section'
1727 );
1728
1729 add_settings_field(
1730 'intro_message',
1731 'Introductory Message',
1732 array($this, 'mxchat_intro_message_callback'),
1733 'mxchat-chatbot',
1734 'mxchat_chatbot_section'
1735 );
1736
1737 add_settings_field(
1738 'input_copy',
1739 'Input Copy',
1740 array($this, 'mxchat_input_copy_callback'),
1741 'mxchat-chatbot',
1742 'mxchat_chatbot_section'
1743 );
1744
1745 add_settings_field(
1746 'rate_limit_logged_in',
1747 __('Rate Limit for Logged-in Users', 'mxchat'),
1748 array($this, 'mxchat_rate_limit_logged_in_callback'),
1749 'mxchat-chatbot',
1750 'mxchat_chatbot_section'
1751 );
1752
1753 add_settings_field(
1754 'rate_limit_logged_out',
1755 __('Rate Limit for Logged-out Users', 'mxchat'),
1756 array($this, 'mxchat_rate_limit_logged_out_callback'),
1757 'mxchat-chatbot',
1758 'mxchat_chatbot_section'
1759 );
1760
1761 add_settings_field(
1762 'rate_limit_message',
1763 'Rate Limit Message',
1764 array($this, 'mxchat_rate_limit_message_callback'),
1765 'mxchat-chatbot',
1766 'mxchat_chatbot_section'
1767 );
1768
1769 add_settings_field(
1770 'pre_chat_message',
1771 'Chat Teaser Pop-up',
1772 array($this, 'mxchat_pre_chat_message_callback'),
1773 'mxchat-chatbot',
1774 'mxchat_chatbot_section'
1775 );
1776
1777 add_settings_field(
1778 'append_to_body',
1779 'Append Chat Widget to Body',
1780 array($this, 'mxchat_append_to_body_callback'),
1781 'mxchat-chatbot',
1782 'mxchat_chatbot_section'
1783 );
1784
1785 add_settings_field(
1786 'privacy_toggle',
1787 'Toggle Privacy Notice',
1788 array($this, 'mxchat_privacy_toggle_callback'),
1789 'mxchat-chatbot',
1790 'mxchat_chatbot_section'
1791 );
1792
1793 add_settings_field(
1794 'complianz_toggle',
1795 'Enable Complianz',
1796 array($this, 'mxchat_complianz_toggle_callback'),
1797 'mxchat-chatbot',
1798 'mxchat_chatbot_section'
1799 );
1800
1801 add_settings_field(
1802 'link_target_toggle',
1803 'Open Links in a New Tab',
1804 array($this, 'mxchat_link_target_toggle_callback'),
1805 'mxchat-chatbot',
1806 'mxchat_chatbot_section'
1807 );
1808
1809 add_settings_field(
1810 'chat_persistence_toggle',
1811 'Enable Chat Persistence',
1812 array($this, 'mxchat_chat_persistence_toggle_callback'),
1813 'mxchat-chatbot',
1814 'mxchat_chatbot_section'
1815 );
1816
1817 add_settings_field(
1818 'popular_question_1',
1819 'Popular Question 1',
1820 array($this, 'mxchat_popular_question_1_callback'),
1821 'mxchat-chatbot',
1822 'mxchat_chatbot_section'
1823 );
1824
1825 add_settings_field(
1826 'popular_question_2',
1827 'Popular Question 2',
1828 array($this, 'mxchat_popular_question_2_callback'),
1829 'mxchat-chatbot',
1830 'mxchat_chatbot_section'
1831 );
1832
1833 add_settings_field(
1834 'popular_question_3',
1835 'Popular Question 3',
1836 array($this, 'mxchat_popular_question_3_callback'),
1837 'mxchat-chatbot',
1838 'mxchat_chatbot_section'
1839 );
1840
1841 add_settings_field(
1842 'additional_popular_questions',
1843 'Additional Popular Questions',
1844 array($this, 'mxchat_additional_popular_questions_callback'),
1845 'mxchat-chatbot',
1846 'mxchat_chatbot_section'
1847 );
1848
1849
1850 // WooCommerce Settings Section
1851 add_settings_section(
1852 'mxchat_woocommerce_section',
1853 'WooCommerce Settings',
1854 null,
1855 'mxchat-embed'
1856 );
1857
1858 // Loops Settings Section
1859 add_settings_section(
1860 'mxchat_loops_section',
1861 'Loops Settings',
1862 null,
1863 'mxchat-embed'
1864 );
1865
1866 // WooCommerce Settings Fields
1867 add_settings_field(
1868 'enable_woocommerce_integration',
1869 'Automatically Embed Products',
1870 array($this, 'mxchat_enable_woocommerce_integration_callback'),
1871 'mxchat-embed',
1872 'mxchat_woocommerce_section'
1873 );
1874
1875
1876 add_settings_field(
1877 'woocommerce_consumer_key',
1878 'WooCommerce Consumer Key',
1879 array($this, 'mxchat_woocommerce_consumer_key_callback'),
1880 'mxchat-embed',
1881 'mxchat_woocommerce_section'
1882 );
1883
1884 add_settings_field(
1885 'woocommerce_consumer_secret',
1886 'WooCommerce Consumer Secret',
1887 array($this, 'mxchat_woocommerce_consumer_secret_callback'),
1888 'mxchat-embed',
1889 'mxchat_woocommerce_section'
1890 );
1891
1892 // Loops Settings Fields
1893 add_settings_field(
1894 'loops_api_key',
1895 'Loops API Key',
1896 array($this, 'mxchat_loops_api_key_callback'),
1897 'mxchat-embed',
1898 'mxchat_loops_section'
1899 );
1900
1901 add_settings_field(
1902 'loops_mailing_list',
1903 'Loops Mailing List',
1904 array($this, 'mxchat_loops_mailing_list_callback'),
1905 'mxchat-embed',
1906 'mxchat_loops_section'
1907 );
1908
1909 add_settings_field(
1910 'triggered_phrase_response',
1911 'Triggered Phrase Response',
1912 array($this, 'mxchat_triggered_phrase_response_callback'),
1913 'mxchat-embed',
1914 'mxchat_loops_section'
1915 );
1916
1917 add_settings_field(
1918 'email_capture_response',
1919 'Email Capture Response',
1920 array($this, 'mxchat_email_capture_response_callback'),
1921 'mxchat-embed',
1922 'mxchat_loops_section'
1923 );
1924
1925
1926 // Brave Search Settings Fields
1927 add_settings_section(
1928 'mxchat_brave_section',
1929 __('Brave Search Settings', 'mxchat'),
1930 array($this, 'mxchat_brave_section_callback'),
1931 'mxchat-embed'
1932 );
1933
1934 add_settings_field(
1935 'brave_api_key',
1936 __('Brave API Key', 'mxchat'),
1937 array($this, 'mxchat_brave_api_key_callback'),
1938 'mxchat-embed',
1939 'mxchat_brave_section'
1940 );
1941
1942 add_settings_field(
1943 'brave_image_count',
1944 __('Number of Images to Return', 'mxchat'),
1945 array($this, 'mxchat_brave_image_count_callback'),
1946 'mxchat-embed',
1947 'mxchat_brave_section'
1948 );
1949
1950 add_settings_field(
1951 'brave_safe_search',
1952 __('Safe Search', 'mxchat'),
1953 array($this, 'mxchat_brave_safe_search_callback'),
1954 'mxchat-embed',
1955 'mxchat_brave_section'
1956 );
1957
1958 add_settings_field(
1959 'brave_news_count',
1960 __('Number of News Articles', 'mxchat'),
1961 array($this, 'mxchat_brave_news_count_callback'),
1962 'mxchat-embed',
1963 'mxchat_brave_section'
1964 );
1965
1966 add_settings_field(
1967 'brave_country',
1968 __('Country', 'mxchat'),
1969 array($this, 'mxchat_brave_country_callback'),
1970 'mxchat-embed',
1971 'mxchat_brave_section'
1972 );
1973
1974 add_settings_field(
1975 'brave_language',
1976 __('Language', 'mxchat'),
1977 array($this, 'mxchat_brave_language_callback'),
1978 'mxchat-embed',
1979 'mxchat_brave_section'
1980 );
1981
1982
1983 // Chat with PDF Intent Settings Fields
1984 add_settings_section(
1985 'mxchat_pdf_intent_section',
1986 __('Toolbar Settings & Intents', 'mxchat'),
1987 array($this, 'mxchat_pdf_intent_section_callback'),
1988 'mxchat-embed'
1989 );
1990
1991 add_settings_field(
1992 'chat_toolbar_toggle',
1993 __('Show Chat Toolbar', 'mxchat'),
1994 array($this, 'mxchat_chat_toolbar_toggle_callback'),
1995 'mxchat-embed',
1996 'mxchat_pdf_intent_section'
1997 );
1998
1999
2000 add_settings_field(
2001 'pdf_intent_trigger_text',
2002 __('Intent Trigger Text', 'mxchat'),
2003 array($this, 'mxchat_pdf_intent_trigger_text_callback'),
2004 'mxchat-embed',
2005 'mxchat_pdf_intent_section'
2006 );
2007
2008 add_settings_field(
2009 'pdf_intent_success_text',
2010 __('Success Text', 'mxchat'),
2011 array($this, 'mxchat_pdf_intent_success_text_callback'),
2012 'mxchat-embed',
2013 'mxchat_pdf_intent_section'
2014 );
2015
2016 add_settings_field(
2017 'pdf_intent_error_text',
2018 __('Error Text', 'mxchat'),
2019 array($this, 'mxchat_pdf_intent_error_text_callback'),
2020 'mxchat-embed',
2021 'mxchat_pdf_intent_section'
2022 );
2023
2024 // Add PDF Maximum Pages Field
2025 add_settings_field(
2026 'pdf_max_pages',
2027 __('Maximum Document Pages', 'mxchat'),
2028 array($this, 'mxchat_pdf_max_pages_callback'),
2029 'mxchat-embed',
2030 'mxchat_pdf_intent_section'
2031 );
2032
2033
2034
2035
2036 // Live Agent Settings Fields
2037 add_settings_section(
2038 'mxchat_live_agent_section',
2039 __('Live Agent Settings', 'mxchat'),
2040 array($this, 'mxchat_live_agent_section_callback'),
2041 'mxchat-embed'
2042 );
2043
2044 // Live Agent Status Fields (add at top of live agent settings)
2045 add_settings_field(
2046 'live_agent_status',
2047 __('Live Agent Status', 'mxchat'),
2048 array($this, 'mxchat_live_agent_status_callback'),
2049 'mxchat-embed',
2050 'mxchat_live_agent_section'
2051 );
2052
2053 add_settings_field(
2054 'live_agent_notification_message',
2055 __('Notification Message', 'mxchat'),
2056 array($this, 'mxchat_live_agent_notification_message_callback'),
2057 'mxchat-embed',
2058 'mxchat_live_agent_section'
2059 );
2060
2061 add_settings_field(
2062 'live_agent_away_message',
2063 __('Away Message', 'mxchat'),
2064 array($this, 'mxchat_live_agent_away_message_callback'),
2065 'mxchat-embed',
2066 'mxchat_live_agent_section'
2067 );
2068
2069 add_settings_field(
2070 'live_agent_webhook_url',
2071 __('Slack Webhook URL', 'mxchat'),
2072 array($this, 'mxchat_live_agent_webhook_url_callback'),
2073 'mxchat-embed',
2074 'mxchat_live_agent_section'
2075 );
2076
2077 add_settings_field(
2078 'live_agent_secret_key',
2079 __('Slack Secret Key', 'mxchat'),
2080 array($this, 'mxchat_live_agent_secret_key_callback'),
2081 'mxchat-embed',
2082 'mxchat_live_agent_section'
2083 );
2084
2085 // Live Agent Integration Fields
2086 add_settings_field(
2087 'live_agent_bot_token',
2088 __('Slack Bot OAuth Token', 'mxchat'),
2089 array($this, 'mxchat_live_agent_bot_token_callback'),
2090 'mxchat-embed',
2091 'mxchat_live_agent_section'
2092 );
2093
2094
2095 // Theme Settings Section
2096 add_settings_section(
2097 'mxchat_theme_section',
2098 'Theme Settings',
2099 null,
2100 'mxchat-theme'
2101 );
2102
2103 add_settings_field(
2104 'close_button_color',
2105 'Close Button & Title Color',
2106 array($this, 'mxchat_close_button_color_callback'),
2107 'mxchat-theme',
2108 'mxchat_theme_section'
2109 );
2110
2111 add_settings_field(
2112 'chatbot_bg_color',
2113 'Chatbot Background Color',
2114 array($this, 'mxchat_chatbot_bg_color_callback'),
2115 'mxchat-theme',
2116 'mxchat_theme_section'
2117 );
2118
2119 add_settings_field(
2120 'user_message_bg_color',
2121 'User Message Background Color',
2122 array($this, 'mxchat_user_message_bg_color_callback'),
2123 'mxchat-theme',
2124 'mxchat_theme_section'
2125 );
2126
2127 add_settings_field(
2128 'user_message_font_color',
2129 'User Message Font Color',
2130 array($this, 'mxchat_user_message_font_color_callback'),
2131 'mxchat-theme',
2132 'mxchat_theme_section'
2133 );
2134
2135 add_settings_field(
2136 'bot_message_bg_color',
2137 'Bot Message Background Color',
2138 array($this, 'mxchat_bot_message_bg_color_callback'),
2139 'mxchat-theme',
2140 'mxchat_theme_section'
2141 );
2142
2143 add_settings_field(
2144 'bot_message_font_color',
2145 'Bot Message Font Color',
2146 array($this, 'mxchat_bot_message_font_color_callback'),
2147 'mxchat-theme',
2148 'mxchat_theme_section'
2149 );
2150
2151 add_settings_field(
2152 'top_bar_bg_color',
2153 'Top Bar Background Color',
2154 array($this, 'mxchat_top_bar_bg_color_callback'),
2155 'mxchat-theme',
2156 'mxchat_theme_section'
2157 );
2158
2159 add_settings_field(
2160 'send_button_font_color',
2161 'Send Button Color',
2162 array($this, 'mxchat_send_button_font_color_callback'),
2163 'mxchat-theme',
2164 'mxchat_theme_section'
2165 );
2166
2167 add_settings_field(
2168 'chat_input_font_color',
2169 'Chat Input Font Color',
2170 array($this, 'mxchat_chat_input_font_color_callback'),
2171 'mxchat-theme',
2172 'mxchat_theme_section'
2173 );
2174
2175 add_settings_field(
2176 'chatbot_background_color',
2177 'Floating Widget Background Color',
2178 array($this, 'mxchat_chatbot_background_color_callback'),
2179 'mxchat-theme',
2180 'mxchat_theme_section'
2181 );
2182
2183 add_settings_field(
2184 'icon_color',
2185 'Chatbot Icon Color',
2186 array($this, 'mxchat_icon_color_callback'),
2187 'mxchat-theme',
2188 'mxchat_theme_section'
2189 );
2190
2191 add_settings_field(
2192 'custom_icon',
2193 'Custom Chatbot Icon (PNG)',
2194 array($this, 'mxchat_custom_icon_callback'),
2195 'mxchat-theme',
2196 'mxchat_theme_section'
2197 );
2198
2199 add_settings_field(
2200 'title_icon',
2201 'Title Bar Icon (PNG)',
2202 array($this, 'mxchat_title_icon_callback'),
2203 'mxchat-theme',
2204 'mxchat_theme_section'
2205 );
2206
2207
2208 add_settings_field(
2209 'live_agent_message_bg_color',
2210 'Live Agent Background Color',
2211 array($this, 'mxchat_live_agent_message_bg_color_callback'),
2212 'mxchat-theme',
2213 'mxchat_theme_section'
2214 );
2215
2216 add_settings_field(
2217 'live_agent_message_font_color',
2218 'Live Agent Font Color',
2219 array($this, 'mxchat_live_agent_message_font_color_callback'),
2220 'mxchat-theme',
2221 'mxchat_theme_section'
2222 );
2223
2224 // Mode Indicator Background Color
2225 add_settings_field(
2226 'mode_indicator_bg_color',
2227 'Mode Indicator Background Color',
2228 array($this, 'mxchat_mode_indicator_bg_color_callback'),
2229 'mxchat-theme',
2230 'mxchat_theme_section'
2231 );
2232
2233 // Mode Indicator Font Color
2234 add_settings_field(
2235 'mode_indicator_font_color',
2236 'Mode Indicator Font Color',
2237 array($this, 'mxchat_mode_indicator_font_color_callback'),
2238 'mxchat-theme',
2239 'mxchat_theme_section'
2240 );
2241
2242 add_settings_field(
2243 'toolbar_icon_color',
2244 'Toolbar Icon Color',
2245 array($this, 'mxchat_toolbar_icon_color_callback'),
2246 'mxchat-theme',
2247 'mxchat_theme_section'
2248 );
2249
2250 // General Settings Section
2251 add_settings_section(
2252 'mxchat_general_section',
2253 'Frequently Asked Questions (FAQ)',
2254 null,
2255 'mxchat-general'
2256 );
2257
2258
2259
2260
2261
2262
2263 }
2264
2265 public function mxchat_handle_activate_license() {
2266 check_ajax_referer('mxchat_activate_license_nonce', 'security');
2267
2268 $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
2269 $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
2270
2271 if (empty($license_key) || empty($customer_email)) {
2272 wp_send_json_error('Email or License Key is missing');
2273 }
2274
2275 $product_id = 'MxChatPRO';
2276
2277 $response = wp_remote_get("http://mxchat.ai/?wc-api=software-api&request=activation&email={$customer_email}&license_key={$license_key}&product_id={$product_id}");
2278
2279 if (is_wp_error($response)) {
2280 wp_send_json_error('Activation failed due to a server error');
2281 }
2282
2283 $body = wp_remote_retrieve_body($response);
2284 $data = json_decode($body);
2285
2286 if ($data && isset($data->activated) && $data->activated) {
2287 update_option('mxchat_license_status', 'active');
2288 update_option('mxchat_pro_email', $customer_email);
2289 update_option('mxchat_activation_key', $license_key);
2290 wp_send_json_success();
2291 } else {
2292 $error_message = isset($data->error) ? $data->error : 'Activation failed';
2293 update_option('mxchat_license_status', 'inactive');
2294 update_option('mxchat_license_error', $error_message);
2295 wp_send_json_error($error_message);
2296 }
2297 }
2298
2299 public function mxchat_rate_limit_logged_in_callback() {
2300 $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' option
2301 $selected_rate_limit = isset($this->options['rate_limit_logged_in']) ? $this->options['rate_limit_logged_in'] : '100';
2302
2303 echo '<div class="pro-feature-wrapper active">';
2304 echo '<select id="rate_limit_logged_in" name="mxchat_options[rate_limit_logged_in]">';
2305 foreach ($rate_limits as $limit) {
2306 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
2307 }
2308 echo '</select>';
2309 echo '<p class="description">Set the maximum number of messages a logged-in user can send within a 24-hour period.</p>';
2310 echo '</div>';
2311 }
2312
2313 public function mxchat_rate_limit_logged_out_callback() {
2314 $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' option
2315 $selected_rate_limit = isset($this->options['rate_limit_logged_out']) ? $this->options['rate_limit_logged_out'] : '10';
2316
2317 echo '<div class="pro-feature-wrapper active">';
2318 echo '<select id="rate_limit_logged_out" name="mxchat_options[rate_limit_logged_out]">';
2319 foreach ($rate_limits as $limit) {
2320 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
2321 }
2322 echo '</select>';
2323 echo '<p class="description">Set the maximum number of messages a logged-out user can send within a 24-hour period.</p>';
2324 echo '</div>';
2325 }
2326
2327
2328 public function mxchat_rate_limit_message_callback() {
2329 // Remove the is_activated check and make it always enabled
2330 echo '<div class="pro-feature-wrapper active">';
2331 printf(
2332 '<textarea id="rate_limit_message" name="mxchat_options[rate_limit_message]" rows="3" cols="50">%s</textarea>',
2333 isset($this->options['rate_limit_message']) ? esc_textarea($this->options['rate_limit_message']) : 'Rate limit exceeded. Please try again later.'
2334 );
2335 echo '</div>';
2336 }
2337
2338
2339 public function mxchat_enable_woocommerce_integration_callback() {
2340 $checked = isset($this->options['enable_woocommerce_integration']) && $this->options['enable_woocommerce_integration'] === '1' ? 'checked' : '';
2341 $disabled = $this->is_activated ? '' : 'disabled';
2342 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2343
2344 echo '<div class="' . esc_attr($class) . '">';
2345 echo '<label class="toggle-switch">';
2346 echo '<input type="checkbox" id="enable_woocommerce_integration" name="mxchat_options[enable_woocommerce_integration]" value="1" ' . $checked . ' ' . $disabled . '>';
2347 echo '<span class="slider"></span>';
2348 echo '</label>';
2349
2350 echo '<p class="description">';
2351 echo 'Automatically syncs new product additions, updates, and removals to the knowledge base. For existing products, submit your product sitemap in the Knowledge Base section to add them initially.';
2352 echo '</p>';
2353
2354 if (!$this->is_activated) {
2355 echo '<div class="pro-feature-overlay">';
2356 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2357 echo '</div>';
2358 }
2359
2360 echo '</div>';
2361 }
2362
2363
2364
2365
2366
2367 private function mxchat_add_option_field($id, $title, $callback = '') {
2368 add_settings_field(
2369 $id,
2370 $title,
2371 $callback ? array($this, $callback) : array($this, $id . '_callback'),
2372 'mxchat-max',
2373 'mxchat_setting_section_id',
2374 $id === 'model' ? ['label_for' => 'model'] : []
2375 );
2376 }
2377
2378 public function api_key_callback() {
2379 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
2380 echo '<input type="password" id="api_key" name="mxchat_options[api_key]" value="' . $apiKey . '" class="regular-text" />';
2381 echo '<button type="button" id="toggleApiKeyVisibility">Show</button>';
2382 echo '<p class="description">The OpenAI API key is required even when using other models as it is used for vector embedding functionality.</p>';
2383 }
2384
2385
2386 public function xai_api_key_callback() {
2387 // Check if the feature is activated (paid feature)
2388 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
2389 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
2390
2391 // Render the input field for the X.AI API key
2392 echo '<div class="' . esc_attr($class) . '">';
2393 printf(
2394 '<input type="password" id="xai_api_key" name="mxchat_options[xai_api_key]" value="%s" class="regular-text" %s />',
2395 isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '',
2396 $disabled
2397 );
2398 echo '<button type="button" id="toggleXaiApiKeyVisibility">Show</button>';
2399
2400 // If the feature is not activated, show the overlay with a "Pro Only" message
2401 if (!$this->is_activated) {
2402 echo '<div class="pro-feature-overlay">';
2403 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2404 echo '</div>';
2405 }
2406
2407 echo '</div>';
2408 }
2409
2410 public function claude_api_key_callback() {
2411 // Check if the feature is activated (paid feature)
2412 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
2413 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
2414
2415 // Render the input field for the Claude API key
2416 echo '<div class="' . esc_attr($class) . '">';
2417 printf(
2418 '<input type="password" id="claude_api_key" name="mxchat_options[claude_api_key]" value="%s" class="regular-text" %s />',
2419 isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '',
2420 $disabled
2421 );
2422 echo '<button type="button" id="toggleClaudeApiKeyVisibility">Show</button>';
2423
2424 // If the feature is not activated, show the overlay with a "Pro Only" message
2425 if (!$this->is_activated) {
2426 echo '<div class="pro-feature-overlay">';
2427 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2428 echo '</div>';
2429 }
2430
2431 echo '</div>';
2432 }
2433
2434 public function mxchat_woocommerce_consumer_key_callback() {
2435 $disabled = $this->is_activated ? '' : 'disabled';
2436 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2437
2438 echo '<div class="' . esc_attr($class) . '">';
2439 printf(
2440 '<input type="text" id="woocommerce_consumer_key" name="mxchat_options[woocommerce_consumer_key]" value="%s" class="regular-text" %s />',
2441 isset($this->options['woocommerce_consumer_key']) ? esc_attr($this->options['woocommerce_consumer_key']) : '',
2442 $disabled
2443 );
2444
2445 if (!$this->is_activated) {
2446 echo '<div class="pro-feature-overlay">';
2447 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2448 echo '</div>';
2449 }
2450
2451 echo '</div>';
2452 }
2453
2454 public function mxchat_woocommerce_consumer_secret_callback() {
2455 $disabled = $this->is_activated ? '' : 'disabled';
2456 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2457
2458 echo '<div class="' . esc_attr($class) . '">';
2459 printf(
2460 '<input type="password" id="woocommerce_consumer_secret" name="mxchat_options[woocommerce_consumer_secret]" value="%s" class="regular-text" %s />',
2461 isset($this->options['woocommerce_consumer_secret']) ? esc_attr($this->options['woocommerce_consumer_secret']) : '',
2462 $disabled
2463 );
2464 echo '<button type="button" id="toggleWooCommerceSecretVisibility">Show</button>';
2465
2466 if (!$this->is_activated) {
2467 echo '<div class="pro-feature-overlay">';
2468 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2469 echo '</div>';
2470 }
2471
2472 echo '</div>';
2473 }
2474
2475 public function mxchat_loops_api_key_callback() {
2476 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
2477
2478 echo '<div class="api-key-wrapper">';
2479 printf(
2480 '<input type="password" id="loops_api_key" name="mxchat_options[loops_api_key]" value="%s" class="regular-text" />',
2481 $loops_api_key
2482 );
2483 echo '<button type="button" id="toggleLoopsApiKeyVisibility">Show</button>';
2484 echo '</div>';
2485 echo '<p class="description">Enter your Loops API Key here. (See FAQ for details)</p>';
2486 }
2487
2488 public function mxchat_loops_mailing_list_callback() {
2489 // Retrieve Loops API key and lists
2490 $loops_api_key = isset($this->options['loops_api_key']) ? $this->options['loops_api_key'] : '';
2491 $selected_list = isset($this->options['loops_mailing_list']) ? $this->options['loops_mailing_list'] : '';
2492
2493 if ($loops_api_key) {
2494 // Fetch lists from Loops API
2495 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
2496
2497 if ($lists) {
2498 echo '<select id="loops_mailing_list" name="mxchat_options[loops_mailing_list]">';
2499 foreach ($lists as $list) {
2500 echo '<option value="' . esc_attr($list['id']) . '" ' . selected($selected_list, $list['id'], false) . '>' . esc_html($list['name']) . '</option>';
2501 }
2502 echo '</select>';
2503 } else {
2504 echo '<p class="description">No lists found. Please verify your API Key.</p>';
2505 }
2506 } else {
2507 echo '<p class="description">Enter a valid Loops API Key to load mailing lists.</p>';
2508 }
2509 }
2510
2511 public function mxchat_triggered_phrase_response_callback() {
2512 $triggered_response = isset($this->options['triggered_phrase_response']) ? $this->options['triggered_phrase_response'] : '';
2513 echo '<textarea id="triggered_phrase_response" name="mxchat_options[triggered_phrase_response]" rows="3" cols="50">' . esc_textarea($triggered_response) . '</textarea>';
2514 echo '<p class="description">Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.</p>';
2515 }
2516
2517 public function mxchat_email_capture_response_callback() {
2518 $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.';
2519 echo '<textarea id="email_capture_response" name="mxchat_options[email_capture_response]" rows="3" cols="50">' . esc_textarea($email_capture_response) . '</textarea>';
2520 echo '<p class="description">Enter the message to send when a user provides their email.</p>';
2521 }
2522
2523
2524 public function mxchat_pre_chat_message_callback() {
2525 printf(
2526 '<textarea id="pre_chat_message" name="mxchat_options[pre_chat_message]" rows="5" cols="50">%s</textarea>',
2527 isset($this->options['pre_chat_message']) ? esc_textarea($this->options['pre_chat_message']) : ''
2528 );
2529 }
2530
2531 // Callback for AI Instructions textarea
2532 public function system_prompt_instructions_callback() {
2533 printf(
2534 '<textarea id="system_prompt_instructions" name="mxchat_options[system_prompt_instructions]" rows="5" cols="50">%s</textarea>',
2535 isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : ''
2536 );
2537 }
2538
2539 public function mxchat_model_callback() {
2540 $models = array(
2541 'X.AI Models' => array(
2542 'grok-beta' => 'grok-beta (Early Beta)'
2543 ),
2544 'Claude Models' => array(
2545 'claude-3-5-sonnet-20241022' => 'Claude 3.5 Sonnet (Most Intelligent)',
2546 'claude-3-opus-20240229' => 'Claude 3 Opus (Highly Complex Tasks)',
2547 'claude-3-sonnet-20240229' => 'Claude 3 Sonnet (Balanced)',
2548 'claude-3-haiku-20240307' => 'Claude 3 Haiku (Fastest)'
2549 ),
2550 'OpenAI Models' => array(
2551 'gpt-4o' => 'GPT-4o (Recommended)',
2552 'gpt-4o-mini' => 'GPT-4o Mini (Fast and Lightweight)',
2553 'gpt-4-turbo' => 'GPT-4 Turbo (High-Performance)',
2554 'gpt-4' => 'GPT-4 (High Intelligence)',
2555 'gpt-3.5-turbo' => 'GPT-3.5 Turbo (Affordable and Fast)'
2556 )
2557 );
2558
2559 $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo';
2560
2561 echo '<select id="model" name="mxchat_options[model]">';
2562
2563 foreach ($models as $group_label => $group_models) {
2564 echo '<optgroup label="' . esc_attr($group_label) . '">';
2565
2566 foreach ($group_models as $model_value => $model_label) {
2567 // Disable if not activated for paid models (Claude or X.AI)
2568 $disabled = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? 'disabled' : '';
2569 $label_suffix = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? ' (Pro Only)' : '';
2570
2571 // Output the <option> element
2572 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
2573 }
2574
2575 echo '</optgroup>';
2576 }
2577
2578 echo '</select>';
2579 }
2580
2581
2582
2583 public function mxchat_top_bar_title_callback() {
2584 printf(
2585 '<input type="text" id="top_bar_title" name="mxchat_options[top_bar_title]" value="%s" />',
2586 isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : ''
2587 );
2588 }
2589
2590 public function mxchat_intro_message_callback() {
2591 printf(
2592 '<textarea id="intro_message" name="mxchat_options[intro_message]" rows="5" cols="50">%s</textarea>',
2593 isset($this->options['intro_message']) ? esc_textarea($this->options['intro_message']) : 'Hello! How can I assist you today?'
2594 );
2595 }
2596
2597 public function mxchat_input_copy_callback() {
2598 printf(
2599 '<input type="text" id="input_copy" name="mxchat_options[input_copy]" value="%s" placeholder="How can I assist?" />',
2600 isset($this->options['input_copy']) ? esc_attr($this->options['input_copy']) : 'How can I assist?'
2601 );
2602 echo '<p class="description">This is the placeholder text for the chat input field.</p>';
2603 }
2604
2605
2606
2607
2608
2609
2610 public function mxchat_close_button_color_callback() {
2611 $disabled = $this->is_activated ? '' : 'disabled';
2612
2613 echo '<div class="pro-feature-wrapper">';
2614 printf(
2615 '<input type="text" id="close_button_color" name="mxchat_options[close_button_color]" value="%s" class="my-color-field" data-default-color="#4a4a4a" %s />',
2616 isset($this->options['close_button_color']) ? esc_attr($this->options['close_button_color']) : '',
2617 esc_attr($disabled)
2618 );
2619
2620 if (!$this->is_activated) {
2621 echo '<div class="pro-feature-overlay">';
2622 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2623 echo '</div>';
2624 }
2625
2626 echo '</div>';
2627 }
2628
2629 public function mxchat_chatbot_bg_color_callback() {
2630 $disabled = $this->is_activated ? '' : 'disabled';
2631
2632 echo '<div class="pro-feature-wrapper">';
2633 printf(
2634 '<input type="text" id="chatbot_bg_color" name="mxchat_options[chatbot_bg_color]" value="%s" class="my-color-field" data-default-color="#f9f9f9" %s />',
2635 isset($this->options['chatbot_bg_color']) ? esc_attr($this->options['chatbot_bg_color']) : '',
2636 esc_attr($disabled)
2637 );
2638
2639 if (!$this->is_activated) {
2640 echo '<div class="pro-feature-overlay">';
2641 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2642 echo '</div>';
2643 }
2644
2645 echo '</div>';
2646 }
2647
2648 public function mxchat_user_message_bg_color_callback() {
2649 $disabled = $this->is_activated ? '' : 'disabled';
2650
2651 echo '<div class="pro-feature-wrapper">';
2652 printf(
2653 '<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 />',
2654 isset($this->options['user_message_bg_color']) ? esc_attr($this->options['user_message_bg_color']) : '',
2655 esc_attr($disabled)
2656 );
2657
2658 if (!$this->is_activated) {
2659 echo '<div class="pro-feature-overlay">';
2660 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2661 echo '</div>';
2662 }
2663
2664 echo '</div>';
2665 }
2666
2667 public function mxchat_user_message_font_color_callback() {
2668 $disabled = $this->is_activated ? '' : 'disabled';
2669
2670 echo '<div class="pro-feature-wrapper">';
2671 printf(
2672 '<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 />',
2673 isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '',
2674 esc_attr($disabled)
2675 );
2676
2677 if (!$this->is_activated) {
2678 echo '<div class="pro-feature-overlay">';
2679 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2680 echo '</div>';
2681 }
2682
2683 echo '</div>';
2684 }
2685
2686 public function mxchat_bot_message_bg_color_callback() {
2687 $disabled = $this->is_activated ? '' : 'disabled';
2688
2689 echo '<div class="pro-feature-wrapper">';
2690 printf(
2691 '<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 />',
2692 isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '',
2693 esc_attr($disabled)
2694 );
2695
2696 if (!$this->is_activated) {
2697 echo '<div class="pro-feature-overlay">';
2698 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2699 echo '</div>';
2700 }
2701
2702 echo '</div>';
2703 }
2704
2705 public function mxchat_bot_message_font_color_callback() {
2706 $disabled = $this->is_activated ? '' : 'disabled';
2707
2708 echo '<div class="pro-feature-wrapper">';
2709 printf(
2710 '<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 />',
2711 isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '',
2712 esc_attr($disabled)
2713 );
2714
2715 if (!$this->is_activated) {
2716 echo '<div class="pro-feature-overlay">';
2717 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2718 echo '</div>';
2719 }
2720
2721 echo '</div>';
2722 }
2723
2724 public function mxchat_live_agent_message_bg_color_callback() {
2725 $disabled = $this->is_activated ? '' : 'disabled';
2726
2727 echo '<div class="pro-feature-wrapper">';
2728 printf(
2729 '<input type="text" id="live_agent_message_bg_color" name="mxchat_options[live_agent_message_bg_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2730 isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '',
2731 esc_attr($disabled)
2732 );
2733
2734 if (!$this->is_activated) {
2735 echo '<div class="pro-feature-overlay">';
2736 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2737 echo '</div>';
2738 }
2739
2740 echo '</div>';
2741 }
2742
2743 public function mxchat_live_agent_message_font_color_callback() {
2744 $disabled = $this->is_activated ? '' : 'disabled';
2745
2746 echo '<div class="pro-feature-wrapper">';
2747 printf(
2748 '<input type="text" id="live_agent_message_font_color" name="mxchat_options[live_agent_message_font_color]" value="%s" class="my-color-field" data-default-color="#333333" %s />',
2749 isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '',
2750 esc_attr($disabled)
2751 );
2752
2753 if (!$this->is_activated) {
2754 echo '<div class="pro-feature-overlay">';
2755 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2756 echo '</div>';
2757 }
2758
2759 echo '</div>';
2760 }
2761
2762 // Mode Indicator Background Color Callback
2763 public function mxchat_mode_indicator_bg_color_callback() {
2764 $disabled = $this->is_activated ? '' : 'disabled';
2765
2766 echo '<div class="pro-feature-wrapper">';
2767 printf(
2768 '<input type="text" id="mode_indicator_bg_color" name="mxchat_options[mode_indicator_bg_color]" value="%s" class="my-color-field" data-default-color="#767676" %s />',
2769 isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '',
2770 esc_attr($disabled)
2771 );
2772
2773 if (!$this->is_activated) {
2774 echo '<div class="pro-feature-overlay">';
2775 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2776 echo '</div>';
2777 }
2778
2779 echo '</div>';
2780 }
2781
2782 // Mode Indicator Font Color Callback
2783 public function mxchat_mode_indicator_font_color_callback() {
2784 $disabled = $this->is_activated ? '' : 'disabled';
2785
2786 echo '<div class="pro-feature-wrapper">';
2787 printf(
2788 '<input type="text" id="mode_indicator_font_color" name="mxchat_options[mode_indicator_font_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2789 isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '',
2790 esc_attr($disabled)
2791 );
2792
2793 if (!$this->is_activated) {
2794 echo '<div class="pro-feature-overlay">';
2795 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2796 echo '</div>';
2797 }
2798
2799 echo '</div>';
2800 }
2801
2802 public function mxchat_toolbar_icon_color_callback() {
2803 $disabled = $this->is_activated ? '' : 'disabled';
2804
2805 echo '<div class="pro-feature-wrapper">';
2806 printf(
2807 '<input type="text" id="toolbar_icon_color" name="mxchat_options[toolbar_icon_color]" value="%s" class="my-color-field" data-default-color="#212121" %s />',
2808 isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '',
2809 esc_attr($disabled)
2810 );
2811
2812 if (!$this->is_activated) {
2813 echo '<div class="pro-feature-overlay">';
2814 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2815 echo '</div>';
2816 }
2817
2818 echo '</div>';
2819 }
2820
2821
2822
2823
2824 public function mxchat_top_bar_bg_color_callback() {
2825 $disabled = $this->is_activated ? '' : 'disabled';
2826
2827 echo '<div class="pro-feature-wrapper">';
2828 printf(
2829 '<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 />',
2830 isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '',
2831 esc_attr($disabled)
2832 );
2833
2834 if (!$this->is_activated) {
2835 echo '<div class="pro-feature-overlay">';
2836 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2837 echo '</div>';
2838 }
2839
2840 echo '</div>';
2841 }
2842
2843 public function mxchat_send_button_font_color_callback() {
2844 $disabled = $this->is_activated ? '' : 'disabled';
2845
2846 echo '<div class="pro-feature-wrapper">';
2847 printf(
2848 '<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 />',
2849 isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '',
2850 esc_attr($disabled)
2851 );
2852
2853 if (!$this->is_activated) {
2854 echo '<div class="pro-feature-overlay">';
2855 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2856 echo '</div>';
2857 }
2858
2859 echo '</div>';
2860 }
2861
2862 public function mxchat_chatbot_background_color_callback() {
2863 $disabled = $this->is_activated ? '' : 'disabled';
2864
2865 echo '<div class="pro-feature-wrapper">';
2866 printf(
2867 '<input type="text" id="chatbot_background_color" name="mxchat_options[chatbot_background_color]" value="%s" class="my-color-field" data-default-color="#000000" %s />',
2868 isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '',
2869 esc_attr($disabled)
2870 );
2871
2872 if (!$this->is_activated) {
2873 echo '<div class="pro-feature-overlay">';
2874 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2875 echo '</div>';
2876 }
2877
2878 echo '</div>';
2879 }
2880
2881 public function mxchat_icon_color_callback() {
2882 $disabled = $this->is_activated ? '' : 'disabled';
2883
2884 echo '<div class="pro-feature-wrapper">';
2885 printf(
2886 '<input type="text" id="icon_color" name="mxchat_options[icon_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2887 isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '',
2888 esc_attr($disabled)
2889 );
2890
2891 if (!$this->is_activated) {
2892 echo '<div class="pro-feature-overlay">';
2893 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2894 echo '</div>';
2895 }
2896
2897 echo '</div>';
2898 }
2899
2900 public function mxchat_custom_icon_callback() {
2901 $disabled = $this->is_activated ? '' : 'disabled';
2902 $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : '';
2903
2904 echo '<div class="pro-feature-wrapper">';
2905 printf(
2906 '<input type="url" id="custom_icon" name="mxchat_options[custom_icon]" value="%s" placeholder="Enter PNG URL" %s />',
2907 $custom_icon_url,
2908 esc_attr($disabled)
2909 );
2910 echo '<p class="description">Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.</p>';
2911
2912 if (!$this->is_activated) {
2913 echo '<div class="pro-feature-overlay">';
2914 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2915 echo '</div>';
2916 }
2917
2918 echo '</div>';
2919 }
2920
2921 public function mxchat_title_icon_callback() {
2922 $disabled = $this->is_activated ? '' : 'disabled';
2923 $title_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['title_icon']) : '';
2924
2925 echo '<div class="pro-feature-wrapper">';
2926 printf(
2927 '<input type="url" id="title_icon" name="mxchat_options[title_icon]" value="%s" placeholder="Enter PNG URL" %s />',
2928 $title_icon_url,
2929 esc_attr($disabled)
2930 );
2931 echo '<p class="description">Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.</p>';
2932
2933 if (!$this->is_activated) {
2934 echo '<div class="pro-feature-overlay">';
2935 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2936 echo '</div>';
2937 }
2938
2939 echo '</div>';
2940 }
2941
2942
2943 public function mxchat_chat_input_font_color_callback() {
2944 $disabled = $this->is_activated ? '' : 'disabled';
2945
2946 echo '<div class="pro-feature-wrapper">';
2947 printf(
2948 '<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 />',
2949 isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '',
2950 esc_attr($disabled)
2951 );
2952
2953 if (!$this->is_activated) {
2954 echo '<div class="pro-feature-overlay">';
2955 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2956 echo '</div>';
2957 }
2958
2959 echo '</div>';
2960 }
2961
2962
2963 public function mxchat_append_to_body_callback() {
2964 $append_to_body_checked = isset($this->options['append_to_body']) && $this->options['append_to_body'] === 'on' ? 'checked' : '';
2965 echo '<label class="toggle-switch">';
2966 printf(
2967 '<input type="checkbox" id="append_to_body" name="mxchat_options[append_to_body]" %s />',
2968 esc_attr($append_to_body_checked)
2969 );
2970 echo '<span class="slider"></span>';
2971 echo '</label>';
2972 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>';
2973 }
2974
2975
2976 public function mxchat_privacy_toggle_callback() {
2977 // Check if the privacy toggle is enabled
2978 $privacy_toggle_checked = isset($this->options['privacy_toggle']) && $this->options['privacy_toggle'] === 'on' ? 'checked' : '';
2979
2980 // Retrieve the stored privacy text if it exists
2981 $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>.';
2982
2983 // Output the toggle switch
2984 echo '<label class="toggle-switch">';
2985 printf(
2986 '<input type="checkbox" id="privacy_toggle" name="mxchat_options[privacy_toggle]" %s />',
2987 esc_attr($privacy_toggle_checked)
2988 );
2989 echo '<span class="slider"></span>';
2990 echo '</label>';
2991 echo '<p class="description">Enable this option to display a privacy notice below the chat widget.</p>';
2992
2993 // Output the custom text input field
2994 printf(
2995 '<textarea id="privacy_text" name="mxchat_options[privacy_text]" rows="5" cols="50" class="regular-text">%s</textarea>',
2996 esc_textarea($privacy_text)
2997 );
2998 echo '<p class="description">Enter the privacy policy text. You can include HTML links.</p>';
2999 }
3000
3001
3002 public function mxchat_complianz_toggle_callback() {
3003 // Check if the Complianz toggle is enabled
3004 $complianz_toggle_checked = isset($this->options['complianz_toggle']) && $this->options['complianz_toggle'] === 'on' ? 'checked' : '';
3005
3006 // Check if the plugin is activated (paid feature)
3007 $disabled = $this->is_activated ? '' : 'disabled';
3008 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3009
3010 echo '<div class="' . esc_attr($class) . '">';
3011
3012 // Output the toggle switch
3013 echo '<label class="toggle-switch">';
3014 printf(
3015 '<input type="checkbox" id="complianz_toggle" name="mxchat_options[complianz_toggle]" %s %s />',
3016 esc_attr($complianz_toggle_checked),
3017 esc_attr($disabled)
3018 );
3019 echo '<span class="slider"></span>';
3020 echo '</label>';
3021 echo '<p class="description">Enable this option to apply Complianz consent logic to the chatbot (must have Complianz Plugin).</p>';
3022
3023 // If the feature is not activated, show the Pro feature overlay
3024 if (!$this->is_activated) {
3025 echo '<div class="pro-feature-overlay">';
3026 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3027 echo '</div>';
3028 }
3029
3030 echo '</div>';
3031 }
3032
3033
3034
3035
3036
3037 public function mxchat_link_target_toggle_callback() {
3038 // Check if the toggle is enabled in the options
3039 $link_target_toggle = isset($this->options['link_target_toggle']) && $this->options['link_target_toggle'] === 'on' ? 'checked' : '';
3040
3041 // Output the toggle switch
3042 echo '<label class="toggle-switch">';
3043 printf(
3044 '<input type="checkbox" id="link_target_toggle" name="mxchat_options[link_target_toggle]" %s />',
3045 esc_attr($link_target_toggle)
3046 );
3047 echo '<span class="slider"></span>';
3048 echo '</label>';
3049 echo '<p class="description">Enable to open links in a new tab (default is to open in the same tab).</p>';
3050 }
3051
3052 public function mxchat_chat_persistence_toggle_callback() {
3053 // Check if chat persistence toggle is enabled
3054 $chat_persistence_toggle_checked = isset($this->options['chat_persistence_toggle']) && $this->options['chat_persistence_toggle'] === 'on' ? 'checked' : '';
3055
3056 // Check if the plugin is activated (paid feature)
3057 $disabled = $this->is_activated ? '' : 'disabled';
3058 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3059
3060 echo '<div class="' . esc_attr($class) . '">';
3061
3062 // Output the toggle switch
3063 echo '<label class="toggle-switch">';
3064 printf(
3065 '<input type="checkbox" id="chat_persistence_toggle" name="mxchat_options[chat_persistence_toggle]" %s %s />',
3066 esc_attr($chat_persistence_toggle_checked),
3067 esc_attr($disabled)
3068 );
3069 echo '<span class="slider"></span>';
3070 echo '</label>';
3071 echo '<p class="description">Enable to keep chat history when users navigate tabs or return to the site within 24 hours.</p>';
3072
3073 // If not activated, show Pro feature overlay
3074 if (!$this->is_activated) {
3075 echo '<div class="pro-feature-overlay">';
3076 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3077 echo '</div>';
3078 }
3079
3080 echo '</div>';
3081 }
3082
3083
3084 public function mxchat_popular_question_1_callback() {
3085 printf(
3086 '<input type="text" id="popular_question_1" name="mxchat_options[popular_question_1]" value="%s" placeholder="Enter Popular Question 1" />',
3087 isset($this->options['popular_question_1']) ? esc_attr($this->options['popular_question_1']) : ''
3088 );
3089 echo '<p class="description">This will be the first popular question in the chatbot.</p>';
3090 }
3091
3092 public function mxchat_popular_question_2_callback() {
3093 // Check if the plugin is activated (paid feature)
3094 $disabled = $this->is_activated ? '' : 'disabled';
3095 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3096
3097 echo '<div class="' . esc_attr($class) . '">';
3098
3099 printf(
3100 '<input type="text" id="popular_question_2" name="mxchat_options[popular_question_2]" value="%s" placeholder="Enter Popular Question 2" %s />',
3101 isset($this->options['popular_question_2']) ? esc_attr($this->options['popular_question_2']) : '',
3102 esc_attr($disabled)
3103 );
3104 echo '<p class="description">This will be the second popular question in the chatbot.</p>';
3105
3106 // If not activated, show Pro feature overlay
3107 if (!$this->is_activated) {
3108 echo '<div class="pro-feature-overlay">';
3109 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3110 echo '</div>';
3111 }
3112
3113 echo '</div>';
3114 }
3115
3116 public function mxchat_popular_question_3_callback() {
3117 // Check if the plugin is activated (paid feature)
3118 $disabled = $this->is_activated ? '' : 'disabled';
3119 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3120
3121 echo '<div class="' . esc_attr($class) . '">';
3122
3123 printf(
3124 '<input type="text" id="popular_question_3" name="mxchat_options[popular_question_3]" value="%s" placeholder="Enter Popular Question 3" %s />',
3125 isset($this->options['popular_question_3']) ? esc_attr($this->options['popular_question_3']) : '',
3126 esc_attr($disabled)
3127 );
3128 echo '<p class="description">This will be the third popular question in the chatbot.</p>';
3129
3130 // If not activated, show Pro feature overlay
3131 if (!$this->is_activated) {
3132 echo '<div class="pro-feature-overlay">';
3133 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3134 echo '</div>';
3135 }
3136
3137 echo '</div>';
3138 }
3139
3140 public function mxchat_additional_popular_questions_callback() {
3141 $disabled = $this->is_activated ? '' : 'disabled';
3142 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3143 $additional_questions = isset($this->options['additional_popular_questions']) ? $this->options['additional_popular_questions'] : array();
3144
3145 echo '<div class="' . esc_attr($class) . '">';
3146 echo '<div id="mxchat-additional-questions-container">';
3147
3148 if (!empty($additional_questions)) {
3149 foreach ($additional_questions as $index => $question) {
3150 printf(
3151 '<div class="mxchat-question-row">
3152 <input type="text" name="mxchat_options[additional_popular_questions][]" value="%s" placeholder="Enter Additional Popular Question %d" %s />
3153 <button type="button" class="button mxchat-remove-question" aria-label="Remove question" %s>Remove</button>
3154 </div>',
3155 esc_attr($question),
3156 $index + 4, // Start numbering from 4
3157 esc_attr($disabled),
3158 esc_attr($disabled)
3159 );
3160 }
3161 } else {
3162 printf(
3163 '<div class="mxchat-question-row">
3164 <input type="text" name="mxchat_options[additional_popular_questions][]" value="" placeholder="Enter Additional Popular Question 4" %s />
3165 <button type="button" class="button mxchat-remove-question" aria-label="Remove question" %s>Remove</button>
3166 </div>',
3167 esc_attr($disabled),
3168 esc_attr($disabled)
3169 );
3170 }
3171
3172 echo '</div>';
3173
3174 printf(
3175 '<button type="button" class="button mxchat-add-question" aria-label="Add question" %s>Add Question</button>',
3176 esc_attr($disabled)
3177 );
3178
3179 echo '<p class="description">You can add more popular questions beyond the default three here.</p>';
3180
3181 if (!$this->is_activated) {
3182 echo '<div class="pro-feature-overlay">';
3183 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3184 echo '</div>';
3185 }
3186
3187 echo '</div>';
3188 }
3189
3190
3191 /**
3192 * Callback for Brave API Key field.
3193 */
3194 public function mxchat_brave_api_key_callback() {
3195 $options = get_option('mxchat_options');
3196 $brave_api_key = isset($options['brave_api_key']) ? esc_attr($options['brave_api_key']) : '';
3197
3198 echo '<div class="api-key-wrapper">';
3199 printf(
3200 '<input type="password" id="brave_api_key" name="mxchat_options[brave_api_key]" value="%s" class="regular-text" />',
3201 $brave_api_key
3202 );
3203 echo '<button type="button" id="toggleBraveApiKeyVisibility" class="button-secondary">Show</button>';
3204 echo '</div>';
3205 echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
3206
3207 ?>
3208 <?php
3209 }
3210
3211 /**
3212 * Callback for Number of Images to Return field.
3213 */
3214 public function mxchat_brave_image_count_callback() {
3215 $options = get_option('mxchat_options');
3216 $brave_image_count = isset($options['brave_image_count']) ? intval($options['brave_image_count']) : 4;
3217
3218 printf(
3219 '<input type="number" id="brave_image_count" name="mxchat_options[brave_image_count]" value="%d" min="1" max="6" />',
3220 $brave_image_count
3221 );
3222 echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
3223 }
3224
3225 /**
3226 * Callback for Safe Search field.
3227 */
3228 public function mxchat_brave_safe_search_callback() {
3229 $options = get_option('mxchat_options');
3230 $brave_safe_search = isset($options['brave_safe_search']) ? esc_attr($options['brave_safe_search']) : 'strict';
3231
3232 ?>
3233 <select id="brave_safe_search" name="mxchat_options[brave_safe_search]">
3234 <option value="strict" <?php selected($brave_safe_search, 'strict'); ?>><?php _e('Strict', 'mxchat'); ?></option>
3235 <option value="off" <?php selected($brave_safe_search, 'off'); ?>><?php _e('Off', 'mxchat'); ?></option>
3236 </select>
3237 <p class="description"><?php _e('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat'); ?></p>
3238 <?php
3239 }
3240
3241
3242 /**
3243 * Callback for Number of News Articles field.
3244 */
3245 public function mxchat_brave_news_count_callback() {
3246 $options = get_option('mxchat_options');
3247 $brave_news_count = isset($options['brave_news_count']) ? intval($options['brave_news_count']) : 3;
3248
3249 printf(
3250 '<input type="number" id="brave_news_count" name="mxchat_options[brave_news_count]" value="%d" min="1" max="10" />',
3251 $brave_news_count
3252 );
3253 echo '<p class="description">' . __('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
3254 }
3255
3256 /**
3257 * Callback for Country field.
3258 */
3259 public function mxchat_brave_country_callback() {
3260 $options = get_option('mxchat_options');
3261 $brave_country = isset($options['brave_country']) ? esc_attr($options['brave_country']) : 'us';
3262
3263 printf(
3264 '<input type="text" id="brave_country" name="mxchat_options[brave_country]" value="%s" maxlength="2" class="small-text" />',
3265 $brave_country
3266 );
3267 echo '<p class="description">' . __('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
3268 }
3269
3270 /**
3271 * Callback for Language field.
3272 */
3273 public function mxchat_brave_language_callback() {
3274 $options = get_option('mxchat_options');
3275 $brave_language = isset($options['brave_language']) ? esc_attr($options['brave_language']) : 'en';
3276
3277 printf(
3278 '<input type="text" id="brave_language" name="mxchat_options[brave_language]" value="%s" maxlength="2" class="small-text" />',
3279 $brave_language
3280 );
3281 echo '<p class="description">' . __('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
3282 }
3283
3284
3285
3286
3287
3288 // Section Callback
3289 public function mxchat_pdf_intent_section_callback() {
3290 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
3291 }
3292
3293 public function mxchat_chat_toolbar_toggle_callback() {
3294 $checked = isset($this->options['chat_toolbar_toggle']) && $this->options['chat_toolbar_toggle'] === 'on' ? 'checked' : '';
3295 $disabled = $this->is_activated ? '' : 'disabled';
3296 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3297
3298 echo '<div class="' . esc_attr($class) . '">';
3299 echo '<label class="toggle-switch">';
3300 printf(
3301 '<input type="checkbox" id="chat_toolbar_toggle" name="mxchat_options[chat_toolbar_toggle]" value="on" %s %s>',
3302 esc_attr($checked),
3303 esc_attr($disabled)
3304 );
3305 echo '<span class="slider"></span>';
3306 echo '</label>';
3307
3308 if (!$this->is_activated) {
3309 echo '<div class="pro-feature-overlay">';
3310 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3311 echo '</div>';
3312 }
3313
3314 echo '</div>';
3315 echo '<p class="description">Enable to display the chat toolbar, adding two icons below the chatbot input field for uploading PDF and Word documents (default is hidden).</p>';
3316 }
3317
3318
3319
3320 // Intent Trigger Text Callback
3321 public function mxchat_pdf_intent_trigger_text_callback() {
3322 $disabled = $this->is_activated ? '' : 'disabled';
3323 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3324
3325 echo '<div class="' . esc_attr($class) . '">';
3326 printf(
3327 '<textarea id="pdf_intent_trigger_text" name="mxchat_options[pdf_intent_trigger_text]" rows="3" cols="50" placeholder="Enter trigger text" %s>%s</textarea>',
3328 esc_attr($disabled),
3329 isset($this->options['pdf_intent_trigger_text']) ? esc_textarea($this->options['pdf_intent_trigger_text']) : ''
3330 );
3331 echo '<p class="description">Text displayed when the intent is triggered.</p>';
3332
3333 if (!$this->is_activated) {
3334 echo '<div class="pro-feature-overlay">';
3335 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3336 echo '</div>';
3337 }
3338 echo '</div>';
3339 }
3340
3341 // Success Text Callback
3342 public function mxchat_pdf_intent_success_text_callback() {
3343 $disabled = $this->is_activated ? '' : 'disabled';
3344 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3345
3346 echo '<div class="' . esc_attr($class) . '">';
3347 printf(
3348 '<textarea id="pdf_intent_success_text" name="mxchat_options[pdf_intent_success_text]" rows="3" cols="50" placeholder="Enter success text" %s>%s</textarea>',
3349 esc_attr($disabled),
3350 isset($this->options['pdf_intent_success_text']) ? esc_textarea($this->options['pdf_intent_success_text']) : ''
3351 );
3352 echo '<p class="description">Text displayed when the intent is successful.</p>';
3353
3354 if (!$this->is_activated) {
3355 echo '<div class="pro-feature-overlay">';
3356 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3357 echo '</div>';
3358 }
3359 echo '</div>';
3360 }
3361
3362 // Error Text Callback
3363 public function mxchat_pdf_intent_error_text_callback() {
3364 $disabled = $this->is_activated ? '' : 'disabled';
3365 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3366
3367 echo '<div class="' . esc_attr($class) . '">';
3368 printf(
3369 '<textarea id="pdf_intent_error_text" name="mxchat_options[pdf_intent_error_text]" rows="3" cols="50" placeholder="Enter error text" %s>%s</textarea>',
3370 esc_attr($disabled),
3371 isset($this->options['pdf_intent_error_text']) ? esc_textarea($this->options['pdf_intent_error_text']) : ''
3372 );
3373 echo '<p class="description">Text displayed when an error occurs during the intent.</p>';
3374
3375 if (!$this->is_activated) {
3376 echo '<div class="pro-feature-overlay">';
3377 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3378 echo '</div>';
3379 }
3380 echo '</div>';
3381 }
3382
3383 // Maximum PDF Pages Callback
3384 public function mxchat_pdf_max_pages_callback() {
3385 $disabled = $this->is_activated ? '' : 'disabled';
3386 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3387 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
3388
3389 echo '<div class="' . esc_attr($class) . '">';
3390 printf(
3391 '<input type="range" id="pdf_max_pages" name="mxchat_options[pdf_max_pages]" min="1" max="69" value="%d" %s oninput="document.getElementById(\'pdf_max_pages_output\').innerText = this.value;" />',
3392 esc_attr($max_pages),
3393 esc_attr($disabled)
3394 );
3395 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
3396 echo '<p class="description">Set the maximum number of document pages users can upload for processing. (1-69 pages)</p>';
3397
3398 if (!$this->is_activated) {
3399 echo '<div class="pro-feature-overlay">';
3400 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3401 echo '</div>';
3402 }
3403 echo '</div>';
3404 }
3405
3406
3407 // Status Toggle Callback
3408 public function mxchat_live_agent_status_callback() {
3409 $disabled = $this->is_activated ? '' : 'disabled';
3410 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3411 $status = isset($this->options['live_agent_status']) ? $this->options['live_agent_status'] : 'offline';
3412
3413 echo '<div class="' . esc_attr($class) . '">';
3414 echo '<label class="mxchat-switch">';
3415 printf(
3416 '<input type="checkbox" id="live_agent_status" name="mxchat_options[live_agent_status]" value="online" %s %s />',
3417 checked($status, 'online', false),
3418 esc_attr($disabled)
3419 );
3420 echo '<span class="mxchat-slider"></span>';
3421 echo '</label>';
3422 echo '<label for="live_agent_status" class="mxchat-status-label">';
3423 echo '<span class="status-text">' . ($status === 'online' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
3424 echo '</label>';
3425
3426 if (!$this->is_activated) {
3427 echo '<div class="pro-feature-overlay">';
3428 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3429 echo '</div>';
3430 }
3431 echo '</div>';
3432 }
3433
3434 // Away Message Callback
3435 public function mxchat_live_agent_away_message_callback() {
3436 $disabled = $this->is_activated ? '' : 'disabled';
3437 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3438 $message = isset($this->options['live_agent_away_message'])
3439 ? $this->options['live_agent_away_message']
3440 : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
3441
3442 echo '<div class="' . esc_attr($class) . '">';
3443 printf(
3444 '<textarea id="live_agent_away_message" name="mxchat_options[live_agent_away_message]" rows="3" cols="50" %s>%s</textarea>',
3445 esc_attr($disabled),
3446 esc_textarea($message)
3447 );
3448 echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
3449
3450 if (!$this->is_activated) {
3451 echo '<div class="pro-feature-overlay">';
3452 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3453 echo '</div>';
3454 }
3455 echo '</div>';
3456 }
3457 public function mxchat_live_agent_notification_message_callback() {
3458 $disabled = $this->is_activated ? '' : 'disabled';
3459 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3460 $message = isset($this->options['live_agent_notification_message'])
3461 ? $this->options['live_agent_notification_message']
3462 : __('Live agent has been notified.', 'mxchat');
3463
3464 echo '<div class="' . esc_attr($class) . '">';
3465 printf(
3466 '<textarea id="live_agent_notification_message" name="mxchat_options[live_agent_notification_message]" rows="3" cols="50" %s>%s</textarea>',
3467 esc_attr($disabled),
3468 esc_textarea($message)
3469 );
3470 echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
3471
3472 if (!$this->is_activated) {
3473 echo '<div class="pro-feature-overlay">';
3474 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3475 echo '</div>';
3476 }
3477 echo '</div>';
3478 }
3479
3480 public function mxchat_live_agent_webhook_url_callback() {
3481 $disabled = $this->is_activated ? '' : 'disabled';
3482 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3483 $webhook_url = isset($this->options['live_agent_webhook_url'])
3484 ? esc_url($this->options['live_agent_webhook_url'])
3485 : esc_url(get_option('live_agent_webhook_url', ''));
3486
3487 echo '<div class="' . esc_attr($class) . '">';
3488 printf(
3489 '<input type="password" id="live_agent_webhook_url" name="mxchat_options[live_agent_webhook_url]" value="%s" class="regular-text" %s />',
3490 $webhook_url,
3491 esc_attr($disabled)
3492 );
3493 echo '<button type="button" id="toggleWebhookUrlVisibility">Show</button>';
3494 echo '<p class="description">Enter your Slack webhook URL for live agent notifications.</p>';
3495 if (!$this->is_activated) {
3496 echo '<div class="pro-feature-overlay">';
3497 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3498 echo '</div>';
3499 }
3500 echo '</div>';
3501 }
3502
3503
3504 public function mxchat_similarity_threshold_callback() {
3505 $threshold = get_option('mxchat_similarity_threshold', 80); // Default to 80
3506 ?>
3507 <input type="range" id="mxchat_similarity_threshold" name="mxchat_similarity_threshold"
3508 min="70" max="85" step="1"
3509 value="<?php echo esc_attr($threshold); ?>"
3510 oninput="document.getElementById('mxchat_threshold_value').textContent = this.value;">
3511 <span id="mxchat_threshold_value"><?php echo esc_html($threshold); ?></span>
3512 <p class="description">
3513 Set the similarity threshold (recommended: 75) to balance accuracy; too high might limit your bot's ability to find relevant knowledge.
3514 </p>
3515 <?php
3516 }
3517
3518
3519
3520 public function mxchat_live_agent_secret_key_callback() {
3521 $disabled = $this->is_activated ? '' : 'disabled';
3522 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3523
3524 echo '<div class="' . esc_attr($class) . '">';
3525 printf(
3526 '<input type="password" id="live_agent_secret_key" name="mxchat_options[live_agent_secret_key]" value="%s" class="regular-text" %s />',
3527 isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : '',
3528 esc_attr($disabled)
3529 );
3530 echo '<button type="button" id="toggleSecretKeyVisibility">Show</button>';
3531 echo '<p class="description">Secret key for validating Slack requests. Keep this secure.</p>';
3532
3533 if (!$this->is_activated) {
3534 echo '<div class="pro-feature-overlay">';
3535 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3536 echo '</div>';
3537 }
3538 echo '</div>';
3539 }
3540
3541 public function mxchat_live_agent_bot_token_callback() {
3542 $disabled = $this->is_activated ? '' : 'disabled';
3543 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3544
3545 echo '<div class="' . esc_attr($class) . '">';
3546 printf(
3547 '<input type="password" id="live_agent_bot_token" name="mxchat_options[live_agent_bot_token]" value="%s" class="regular-text" %s />',
3548 isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : '',
3549 esc_attr($disabled)
3550 );
3551 echo '<button type="button" id="toggleBotTokenVisibility">Show</button>';
3552 echo '<p class="description">Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.</p>';
3553
3554 if (!$this->is_activated) {
3555 echo '<div class="pro-feature-overlay">';
3556 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3557 echo '</div>';
3558 }
3559 echo '</div>';
3560 }
3561
3562 public function mxchat_enqueue_admin_assets() {
3563 wp_enqueue_style('wp-color-picker');
3564
3565 // Get the plugin version or file modification time for cache busting
3566 $plugin_version = '1.5.7'; // Replace this with your plugin's version
3567
3568 // File paths
3569 $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
3570 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
3571 $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
3572 $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
3573
3574 // Check if files exist and get modification times
3575 $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
3576 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
3577 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
3578 $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
3579
3580 // Enqueue scripts and styles with corrected paths
3581 wp_enqueue_script(
3582 'mxchat-color-picker',
3583 plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
3584 array('wp-color-picker'),
3585 $color_picker_version,
3586 true
3587 );
3588
3589 wp_enqueue_script(
3590 'mxchat-embedding-check',
3591 plugin_dir_url(__FILE__) . '../js/embedding-check.js',
3592 array(),
3593 $embedding_check_version,
3594 true
3595 );
3596
3597 wp_enqueue_script(
3598 'mxchat-transcripts-js',
3599 plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
3600 array('jquery'),
3601 $transcripts_js_version,
3602 true
3603 );
3604
3605 wp_enqueue_script(
3606 'mxchat-admin-js',
3607 plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
3608 array('jquery'),
3609 $plugin_version,
3610 true
3611 );
3612
3613
3614
3615 // Localize the script with data for the activation process
3616 wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
3617 'ajax_url' => admin_url('admin-ajax.php'),
3618 'nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
3619 ));
3620
3621 wp_enqueue_style(
3622 'mxchat-admin-css',
3623 plugin_dir_url(__FILE__) . '../css/admin-style.css',
3624 array(),
3625 $admin_css_version
3626 );
3627
3628 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
3629 'ajax_url' => admin_url('admin-ajax.php'),
3630 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
3631 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
3632 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
3633 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
3634 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
3635 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
3636 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
3637 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
3638 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
3639 'icon_color' => $this->options['icon_color'] ?? '#fff',
3640 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
3641 'pre_chat_message' => $this->options['pre_chat_message'] ?? 'Hey there! Ask me anything!',
3642 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
3643
3644 // New fields for Loops Integration
3645 'loops_api_key' => $this->options['loops_api_key'] ?? '',
3646 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
3647 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? 'Would you like to join our mailing list? Please provide your email below.',
3648 'email_capture_response' => $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.',
3649 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? "Please provide the URL to the PDF you'd like to discuss.",
3650 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? "I've processed the PDF. What questions do you have about it?",
3651 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? "Sorry, I couldn't process the PDF. Please ensure it's a valid file.",
3652 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
3653
3654 // Live Agent Integration
3655 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
3656 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
3657 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
3658
3659 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
3660 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
3661 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
3662 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
3663 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
3664 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
3665
3666 ));
3667
3668 // Localize the script to pass the nonce and other data to JavaScript
3669 wp_localize_script('mxchat-admin-js', 'mxchatInlineEdit', array(
3670 'nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
3671 'ajax_url' => admin_url('admin-ajax.php')
3672 ));
3673
3674
3675 }
3676
3677 public function mxchat_sanitize($input) {
3678 $new_input = array();
3679
3680 if (isset($input['api_key'])) {
3681 $new_input['api_key'] = sanitize_text_field($input['api_key']);
3682 }
3683
3684 if (isset($input['similarity_threshold'])) {
3685 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
3686 $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 70), 85); // Enforce range
3687 }
3688
3689 if (isset($input['xai_api_key'])) {
3690 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
3691 }
3692
3693 if (isset($input['claude_api_key'])) {
3694 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
3695 }
3696
3697 if (isset($input['enable_woocommerce_integration'])) {
3698 $new_input['enable_woocommerce_integration'] = isset($input['enable_woocommerce_integration']) && $input['enable_woocommerce_integration'] === '1' ? '1' : '0';
3699
3700 }
3701
3702 if (isset($input['privacy_toggle'])) {
3703 $new_input['privacy_toggle'] = $input['privacy_toggle'];
3704 }
3705
3706 if (isset($input['complianz_toggle'])) {
3707 $new_input['complianz_toggle'] = $input['complianz_toggle'];
3708 }
3709
3710 // Handle custom privacy text input
3711 if (isset($input['privacy_text'])) {
3712 // Allow basic HTML for links
3713 $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
3714 }
3715
3716 if (isset($input['system_prompt_instructions'])) {
3717 $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
3718 }
3719
3720 if (isset($input['mxchat_pro_email'])) {
3721 $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
3722 }
3723
3724 if (isset($input['mxchat_activation_key'])) {
3725 $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
3726 }
3727
3728 if (isset($input['append_to_body'])) {
3729 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
3730 }
3731
3732 if (isset($input['top_bar_title'])) {
3733 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
3734 }
3735
3736 if (isset($input['intro_message'])) {
3737 $new_input['intro_message'] = sanitize_text_field($input['intro_message']);
3738 }
3739
3740 if (isset($input['input_copy'])) {
3741 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
3742 }
3743
3744
3745 if (isset($input['rate_limit_message'])) {
3746 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
3747 }
3748
3749 if (isset($input['rate_limit_logged_in'])) {
3750 $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
3751 $rate_limit = sanitize_text_field($input['rate_limit_logged_in']);
3752
3753 if (in_array($rate_limit, $allowed_limits, true)) {
3754 $new_input['rate_limit_logged_in'] = $rate_limit;
3755 } else {
3756 $new_input['rate_limit_logged_in'] = '100'; // Default for logged-in users
3757 }
3758 }
3759
3760 if (isset($input['rate_limit_logged_out'])) {
3761 $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
3762 $rate_limit = sanitize_text_field($input['rate_limit_logged_out']);
3763
3764 if (in_array($rate_limit, $allowed_limits, true)) {
3765 $new_input['rate_limit_logged_out'] = $rate_limit;
3766 } else {
3767 $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users
3768 }
3769 }
3770
3771 if (isset($input['pre_chat_message'])) {
3772 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
3773 }
3774
3775 if (isset($input['model'])) {
3776 $allowed_models = array(
3777 'grok-beta',
3778 'claude-3-5-sonnet-20241022',
3779 'claude-3-opus-20240229',
3780 'claude-3-sonnet-20240229',
3781 'claude-3-haiku-20240307',
3782 'gpt-4o',
3783 'gpt-4o-mini',
3784 'gpt-4-turbo',
3785 'gpt-4',
3786 'gpt-3.5-turbo',
3787 );
3788 if (in_array($input['model'], $allowed_models)) {
3789 $new_input['model'] = sanitize_text_field($input['model']);
3790 }
3791 }
3792
3793 // Sanitize new pro features
3794 if (isset($input['close_button_color'])) {
3795 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
3796 }
3797
3798 if (isset($input['chatbot_bg_color'])) {
3799 $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
3800 }
3801
3802 if (isset($input['woocommerce_consumer_key'])) {
3803 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
3804 }
3805
3806 if (isset($input['woocommerce_consumer_secret'])) {
3807 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
3808 }
3809
3810 if (isset($input['user_message_bg_color'])) {
3811 $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
3812 }
3813
3814 if (isset($input['user_message_font_color'])) {
3815 $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
3816 }
3817
3818 if (isset($input['bot_message_bg_color'])) {
3819 $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
3820 }
3821
3822 if (isset($input['bot_message_font_color'])) {
3823 $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
3824 }
3825
3826 if (isset($input['live_agent_message_bg_color'])) {
3827 $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']);
3828 }
3829
3830 if (isset($input['live_agent_message_font_color'])) {
3831 $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']);
3832 }
3833
3834 if (isset($input['mode_indicator_bg_color'])) {
3835 $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']);
3836 }
3837
3838 if (isset($input['mode_indicator_font_color'])) {
3839 $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']);
3840 }
3841
3842 if (isset($input['toolbar_icon_color'])) {
3843 $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']);
3844 }
3845
3846 if (isset($input['top_bar_bg_color'])) {
3847 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
3848 }
3849
3850 if (isset($input['send_button_font_color'])) {
3851 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
3852 }
3853
3854 if (isset($input['chatbot_background_color'])) {
3855 $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
3856 }
3857
3858 if (isset($input['icon_color'])) {
3859 $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
3860 }
3861
3862 if (isset($input['custom_icon'])) {
3863 $new_input['custom_icon'] = esc_url_raw($input['custom_icon']);
3864 }
3865
3866
3867 if (isset($input['title_icon'])) {
3868 $new_input['title_icon'] = esc_url_raw($input['title_icon']);
3869 }
3870
3871 if (isset($input['chat_input_font_color'])) {
3872 $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
3873 }
3874
3875 // Sanitize link_target_toggle
3876 if (isset($input['link_target_toggle'])) {
3877 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
3878 }
3879
3880 // Sanitize Loops API Key
3881 if (isset($input['loops_api_key'])) {
3882 $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
3883 }
3884
3885 if (isset($input['chat_persistence_toggle'])) {
3886 $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
3887 }
3888
3889
3890
3891 if (isset($input['popular_question_1'])) {
3892 $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
3893 }
3894
3895 if (isset($input['popular_question_2'])) {
3896 $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
3897 }
3898
3899 if (isset($input['popular_question_3'])) {
3900 $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
3901 }
3902
3903 if (isset($input['additional_popular_questions']) && is_array($input['additional_popular_questions'])) {
3904 $new_input['additional_popular_questions'] = array_map('sanitize_text_field', $input['additional_popular_questions']);
3905 }
3906
3907 // Sanitize Loops Mailing List
3908 if (isset($input['loops_mailing_list'])) {
3909 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
3910 }
3911
3912 // Sanitize Triggered Phrase Response
3913 if (isset($input['triggered_phrase_response'])) {
3914 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
3915 }
3916
3917 if (isset($input['email_capture_response'])) {
3918 $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
3919 }
3920
3921
3922 // Sanitize Brave Search Settings
3923 if (isset($input['brave_api_key'])) {
3924 $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
3925 }
3926
3927 if (isset($input['brave_image_count'])) {
3928 $image_count = intval($input['brave_image_count']);
3929 $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
3930 }
3931
3932 if (isset($input['brave_safe_search'])) {
3933 $allowed = array('strict', 'off');
3934 $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
3935 }
3936
3937 if (isset($input['brave_news_count'])) {
3938 $news_count = intval($input['brave_news_count']);
3939 $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
3940 }
3941
3942 if (isset($input['brave_country'])) {
3943 $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
3944 }
3945
3946 if (isset($input['brave_language'])) {
3947 $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
3948 }
3949
3950
3951 if (isset($input['chat_toolbar_toggle'])) {
3952 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
3953 }
3954
3955
3956 if (isset($input['pdf_intent_trigger_text'])) {
3957 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
3958 }
3959
3960 if (isset($input['pdf_intent_success_text'])) {
3961 $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']);
3962 }
3963
3964 if (isset($input['pdf_intent_error_text'])) {
3965 $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']);
3966 }
3967
3968 if (isset($input['pdf_max_pages'])) {
3969 $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']);
3970 if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) {
3971 $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range
3972 }
3973 }
3974
3975
3976 if (isset($input['live_agent_webhook_url'])) {
3977 $new_input['live_agent_webhook_url'] = esc_url_raw($input['live_agent_webhook_url']);
3978 }
3979 if (isset($input['live_agent_secret_key'])) {
3980 $new_input['live_agent_secret_key'] = sanitize_text_field($input['live_agent_secret_key']);
3981 }
3982
3983 // Live Agent Integration
3984 if (isset($input['live_agent_bot_token'])) {
3985 $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
3986 }
3987
3988 if (isset($input['live_agent_status'])) {
3989 $new_input['live_agent_status'] = ($input['live_agent_status'] === 'online') ? 'online' : 'offline';
3990 }
3991 if (isset($input['live_agent_away_message'])) {
3992 $new_input['live_agent_away_message'] = sanitize_textarea_field($input['live_agent_away_message']);
3993 }
3994 if (isset($input['live_agent_notification_message'])) {
3995 $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']);
3996 }
3997
3998
3999 return $new_input;
4000 }
4001
4002
4003 // Method to append the chatbot to the body
4004 public function mxchat_append_chatbot_to_body() {
4005 $options = get_option('mxchat_options');
4006 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
4007 echo do_shortcode('[mxchat_chatbot floating="yes"]');
4008 }
4009 }
4010
4011
4012
4013 private static function mxchat_extract_main_content($html) {
4014 $dom = new DOMDocument;
4015 libxml_use_internal_errors(true); // Suppress HTML parsing errors
4016 @$dom->loadHTML($html);
4017 libxml_clear_errors();
4018
4019 $xpath = new DOMXPath($dom);
4020
4021 // Simplified selectors focusing on common content areas
4022 $selectors = [
4023 '//article',
4024 '//*[@id="content"]',
4025 '//*[@class="entry-content"]',
4026 '//main',
4027 ];
4028
4029 foreach ($selectors as $selector) {
4030 $nodes = $xpath->query($selector);
4031 if ($nodes->length > 0) {
4032 $content = '';
4033 foreach ($nodes as $node) {
4034 $content .= $dom->saveHTML($node);
4035 }
4036 return $content;
4037 }
4038 }
4039
4040 // Fallback: Return the entire body content if no specific selector matches
4041 $body = $dom->getElementsByTagName('body');
4042 return $body->length > 0 ? $dom->saveHTML($body->item(0)) : $html;
4043 }
4044
4045 public function mxchat_handle_sitemap_submission() {
4046 // Check if the form was submitted and verify permissions
4047 if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
4048 wp_die(esc_html__('Unauthorized access', 'mxchat'));
4049 }
4050
4051 // Verify nonce
4052 check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
4053
4054 // Validate URL
4055 if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
4056 set_transient('mxchat_admin_notice_error',
4057 esc_html__('Please provide a valid URL.', 'mxchat'),
4058 30
4059 );
4060 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
4061 exit;
4062 }
4063
4064 $submitted_url = esc_url_raw($_POST['sitemap_url']);
4065 $response = wp_remote_get($submitted_url, array('timeout' => 30));
4066
4067 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
4068 $error_message = is_wp_error($response) ? $response->get_error_message() : 'Failed to fetch URL';
4069 set_transient('mxchat_admin_notice_error',
4070 sprintf(
4071 esc_html__('Failed to fetch the URL: %s', 'mxchat'),
4072 esc_html($error_message)
4073 ),
4074 30
4075 );
4076 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
4077 exit;
4078 }
4079
4080 $content_type = wp_remote_retrieve_header($response, 'content-type');
4081 $body_content = wp_remote_retrieve_body($response);
4082
4083 if (empty($body_content)) {
4084 set_transient('mxchat_admin_notice_error',
4085 esc_html__('Empty response received from URL.', 'mxchat'),
4086 30
4087 );
4088 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
4089 exit;
4090 }
4091
4092 // Handle PDF URL
4093 if ($this->is_pdf_url($submitted_url, $response)) {
4094 $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
4095
4096 if ($result === 'scheduled') {
4097 set_transient(
4098 'mxchat_last_pdf_url',
4099 sanitize_text_field($submitted_url),
4100 DAY_IN_SECONDS
4101 );
4102 set_transient('mxchat_admin_notice_info',
4103 esc_html__('PDF processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
4104 30
4105 );
4106 } else {
4107 set_transient('mxchat_admin_notice_error',
4108 esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
4109 30
4110 );
4111 }
4112
4113 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
4114 exit;
4115 }
4116
4117 // Handle Sitemap XML
4118 if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
4119 libxml_use_internal_errors(true);
4120 $xml = simplexml_load_string($body_content);
4121 $xml_errors = libxml_get_errors();
4122 libxml_clear_errors();
4123
4124 if ($xml === false || !empty($xml_errors)) {
4125 set_transient('mxchat_admin_notice_error',
4126 esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
4127 30
4128 );
4129 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
4130 exit;
4131 }
4132
4133 $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
4134
4135 if ($result === 'scheduled') {
4136 set_transient(
4137 'mxchat_last_sitemap_url',
4138 sanitize_text_field($submitted_url),
4139 DAY_IN_SECONDS
4140 );
4141 set_transient('mxchat_admin_notice_info',
4142 esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
4143 30
4144 );
4145 } else {
4146 set_transient('mxchat_admin_notice_error',
4147 esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'),
4148 30
4149 );
4150 }
4151
4152 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
4153 exit;
4154 }
4155
4156 // Handle Regular URL
4157 $page_content = $this->mxchat_extract_main_content($body_content);
4158 $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
4159
4160 if (empty($sanitized_content)) {
4161 set_transient('mxchat_admin_notice_error',
4162 esc_html__('No valid content found on the provided URL.', 'mxchat'),
4163 30
4164 );
4165 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
4166 exit;
4167 }
4168
4169 $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
4170 if (is_array($embedding_vector)) {
4171 MxChat_Utils::submit_content_to_db(
4172 $sanitized_content,
4173 $submitted_url,
4174 $this->options['api_key']
4175 );
4176 set_transient('mxchat_admin_notice_success',
4177 esc_html__('URL content successfully submitted!', 'mxchat'),
4178 30
4179 );
4180 } else {
4181 set_transient('mxchat_admin_notice_error',
4182 esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'),
4183 30
4184 );
4185 }
4186
4187 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
4188 exit;
4189 }
4190 private function is_pdf_url($url, $response) {
4191 $content_type = wp_remote_retrieve_header($response, 'content-type');
4192 $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
4193
4194 return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf';
4195 }
4196
4197 private function handle_pdf_for_knowledge_base($pdf_url, $response) {
4198 if (!current_user_can('manage_options')) {
4199 //error_log('Unauthorized PDF processing attempt');
4200 return false;
4201 }
4202
4203 $pdf_url = esc_url_raw($pdf_url);
4204 $upload_dir = wp_upload_dir();
4205
4206 if (isset($upload_dir['error']) && $upload_dir['error'] !== false) {
4207 //error_log(sprintf('Upload directory error: %s', esc_html($upload_dir['error'])));
4208 return false;
4209 }
4210
4211 $pdf_filename = sanitize_file_name('mxchat_kb_' . time() . '.pdf');
4212 $pdf_path = trailingslashit($upload_dir['path']) . $pdf_filename;
4213
4214 $response_body = wp_remote_retrieve_body($response);
4215 if (empty($response_body)) {
4216 //error_log('Empty PDF response body');
4217 return false;
4218 }
4219
4220 if (!wp_mkdir_p(dirname($pdf_path))) {
4221 //error_log(sprintf('Failed to create directory for PDF: %s', esc_html($pdf_path)));
4222 return false;
4223 }
4224
4225 try {
4226 file_put_contents($pdf_path, $response_body);
4227
4228 if (!file_exists($pdf_path)) {
4229 throw new Exception('Failed to save PDF file');
4230 }
4231
4232 $parser = new \Smalot\PdfParser\Parser();
4233 $pdf = $parser->parseFile($pdf_path);
4234 $total_pages = absint(count($pdf->getPages()));
4235
4236 if ($total_pages < 1) {
4237 throw new Exception('Invalid PDF: no pages found');
4238 }
4239
4240 wp_schedule_single_event(time(), 'mxchat_process_pdf_pages', array(
4241 'pdf_path' => $pdf_path,
4242 'pdf_url' => $pdf_url,
4243 'total_pages' => $total_pages,
4244 'batch_size' => absint(15),
4245 'batch_pause' => absint(10)
4246 ));
4247
4248 $status_data = array(
4249 'total_pages' => $total_pages,
4250 'processed_pages' => 0,
4251 'status' => 'processing',
4252 'last_update' => time()
4253 );
4254
4255 set_transient(
4256 sanitize_key('mxchat_pdf_status_' . md5($pdf_url)),
4257 array_map('sanitize_text_field', $status_data),
4258 DAY_IN_SECONDS
4259 );
4260
4261 return 'scheduled';
4262
4263 } catch (Exception $e) {
4264 //error_log(sprintf('Error preparing PDF for processing: %s', esc_html($e->getMessage())));
4265 if (file_exists($pdf_path)) {
4266 wp_delete_file($pdf_path);
4267 }
4268 return false;
4269 }
4270 }
4271 public static function process_pdf_pages_cron($pdf_path, $pdf_url, $total_pages, $batch_size, $batch_pause) {
4272 // Validate inputs
4273 $pdf_path = sanitize_text_field($pdf_path);
4274 $pdf_url = esc_url_raw($pdf_url);
4275 $total_pages = absint($total_pages);
4276 $batch_size = absint($batch_size);
4277 $batch_pause = absint($batch_pause);
4278
4279 try {
4280 if (!file_exists($pdf_path)) {
4281 throw new Exception(sprintf('PDF file not found at path: %s', esc_html($pdf_path)));
4282 }
4283
4284 $parser = new \Smalot\PdfParser\Parser();
4285 $pdf = $parser->parseFile($pdf_path);
4286 $pages = $pdf->getPages();
4287
4288 // Get current progress
4289 $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url));
4290 $status = get_transient($status_key);
4291
4292 if (!$status || !is_array($status)) {
4293 throw new Exception('Invalid status data retrieved from transient');
4294 }
4295
4296 $start_page = absint($status['processed_pages']);
4297 $end_page = min($start_page + $batch_size, $total_pages);
4298
4299 $instance = new self(); // Create an instance of the class
4300
4301 for ($i = $start_page; $i < $end_page; $i++) {
4302 $text = $pages[$i]->getText();
4303 $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
4304
4305 if (!empty($sanitized_content)) {
4306 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
4307 if (is_array($embedding_vector)) {
4308 $metadata = array(
4309 'document_type' => 'pdf',
4310 'total_pages' => $total_pages,
4311 'current_page' => $i + 1,
4312 'prev_page' => $i > 0 ? $i : null,
4313 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
4314 'source_url' => $pdf_url
4315 );
4316
4317 $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
4318 $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
4319
4320 $options = get_option('mxchat_options');
4321 MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
4322 }
4323 }
4324
4325 // Update progress with sanitized data
4326 $status['processed_pages'] = absint($i + 1);
4327 $status['last_update'] = time();
4328 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4329 }
4330
4331 // Schedule next batch if needed
4332 if ($end_page < $total_pages) {
4333 wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_pdf_pages', array(
4334 'pdf_path' => $pdf_path,
4335 'pdf_url' => $pdf_url,
4336 'total_pages' => $total_pages,
4337 'batch_size' => $batch_size,
4338 'batch_pause' => $batch_pause
4339 ));
4340 } else {
4341 // Processing complete
4342 $status['status'] = 'complete';
4343 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4344 if (file_exists($pdf_path)) {
4345 wp_delete_file($pdf_path);
4346 }
4347 }
4348
4349 } catch (\Exception $e) {
4350 $status['status'] = 'error';
4351 $status['error'] = sanitize_text_field($e->getMessage());
4352 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4353 if (file_exists($pdf_path)) {
4354 wp_delete_file($pdf_path);
4355 }
4356 }
4357 }
4358
4359 public function get_pdf_processing_status($pdf_url) {
4360 $pdf_url = esc_url_raw($pdf_url);
4361 $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
4362
4363 if (!$status || !is_array($status)) {
4364 return false;
4365 }
4366
4367 return array(
4368 'total_pages' => absint($status['total_pages']),
4369 'processed_pages' => absint($status['processed_pages']),
4370 'percentage' => round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100),
4371 'status' => sanitize_text_field($status['status']),
4372 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ago'
4373 );
4374 }
4375
4376 private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) {
4377 if (!current_user_can('manage_options')) {
4378 //error_log('Unauthorized sitemap processing attempt');
4379 return false;
4380 }
4381
4382 try {
4383 $sitemap_url = esc_url_raw($sitemap_url);
4384
4385 if (!$xml || !is_object($xml)) {
4386 throw new Exception('Invalid XML object provided');
4387 }
4388
4389 $urls = [];
4390 foreach ($xml->url as $url_element) {
4391 $url = esc_url_raw((string)$url_element->loc);
4392 if ($url) {
4393 $urls[] = $url;
4394 }
4395 }
4396
4397 $total_urls = absint(count($urls));
4398
4399 if ($total_urls < 1) {
4400 throw new Exception('No valid URLs found in sitemap');
4401 }
4402
4403 wp_schedule_single_event(time(), 'mxchat_process_sitemap_urls', array(
4404 'urls' => $urls,
4405 'sitemap_url' => $sitemap_url,
4406 'total_urls' => $total_urls,
4407 'batch_size' => absint(10),
4408 'batch_pause' => absint(5)
4409 ));
4410
4411 $status_data = array(
4412 'total_urls' => $total_urls,
4413 'processed_urls' => 0,
4414 'status' => 'processing',
4415 'last_update' => time()
4416 );
4417
4418 set_transient(
4419 sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
4420 array_map('sanitize_text_field', $status_data),
4421 DAY_IN_SECONDS
4422 );
4423
4424 return 'scheduled';
4425
4426 } catch (\Exception $e) {
4427 //error_log(sprintf('Error preparing sitemap for processing: %s', esc_html($e->getMessage())));
4428 return false;
4429 }
4430 }
4431 public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
4432 // Validate inputs
4433 $sitemap_url = esc_url_raw($sitemap_url);
4434 $total_urls = absint($total_urls);
4435 $batch_size = absint($batch_size);
4436 $batch_pause = absint($batch_pause);
4437
4438 if (!is_array($urls) || empty($urls)) {
4439 return;
4440 }
4441
4442 try {
4443 $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url));
4444 $status = get_transient($status_key);
4445
4446 if (!$status || !is_array($status)) {
4447 throw new Exception('Invalid status data retrieved from transient');
4448 }
4449
4450 $start_url = absint($status['processed_urls']);
4451 $end_url = min($start_url + $batch_size, $total_urls);
4452
4453 $instance = new self(); // Create an instance of the class
4454 $embedding_success = true;
4455
4456 for ($i = $start_url; $i < $end_url; $i++) {
4457 $page_url = esc_url_raw($urls[$i]);
4458 $page_response = wp_remote_get($page_url);
4459
4460 if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
4461 continue;
4462 }
4463
4464 $page_html = wp_remote_retrieve_body($page_response);
4465 $page_content = $instance->mxchat_extract_main_content($page_html); // Call via instance
4466 $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content); // Call via instance
4467
4468 if (!empty($sanitized_content)) {
4469 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
4470 if (is_array($embedding_vector)) {
4471 $options = get_option('mxchat_options');
4472 MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
4473 } else {
4474 $embedding_success = false;
4475 }
4476 }
4477
4478 $status['processed_urls'] = absint($i + 1);
4479 $status['last_update'] = time();
4480 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4481 }
4482
4483 if ($end_url < $total_urls) {
4484 wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
4485 'urls' => array_map('esc_url_raw', $urls),
4486 'sitemap_url' => $sitemap_url,
4487 'total_urls' => $total_urls,
4488 'batch_size' => $batch_size,
4489 'batch_pause' => $batch_pause,
4490 ));
4491 } else {
4492 $status['status'] = 'complete';
4493 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4494 }
4495 } catch (\Exception $e) {
4496 $status['status'] = 'error';
4497 $status['error'] = sanitize_text_field($e->getMessage());
4498 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4499 }
4500 }
4501
4502 public function get_sitemap_processing_status($sitemap_url) {
4503 $sitemap_url = esc_url_raw($sitemap_url);
4504 $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
4505
4506 if (!$status || !is_array($status)) {
4507 return false;
4508 }
4509
4510 return array(
4511 'total_urls' => absint($status['total_urls']),
4512 'processed_urls' => absint($status['processed_urls']),
4513 'percentage' => round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100),
4514 'status' => sanitize_text_field($status['status']),
4515 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ago'
4516 );
4517 }
4518 public function mxchat_stop_processing() {
4519 // Verify permissions
4520 if (!current_user_can('manage_options')) {
4521 wp_die(esc_html__('Unauthorized access', 'mxchat'));
4522 }
4523
4524 // Verify nonce
4525 check_admin_referer('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce');
4526
4527 // Get the last sitemap URL and clear its transient
4528 $sitemap_url = get_transient('mxchat_last_sitemap_url');
4529 if ($sitemap_url) {
4530 delete_transient('mxchat_sitemap_status_' . md5($sitemap_url));
4531 delete_transient('mxchat_last_sitemap_url');
4532 }
4533
4534 // Get the last PDF URL and clear its transient
4535 $pdf_url = get_transient('mxchat_last_pdf_url');
4536 if ($pdf_url) {
4537 delete_transient('mxchat_pdf_status_' . md5($pdf_url));
4538 delete_transient('mxchat_last_pdf_url');
4539 }
4540
4541 // Unschedule any pending sitemap events
4542 $timestamp = wp_next_scheduled('mxchat_process_sitemap_urls');
4543 if ($timestamp) {
4544 wp_unschedule_event($timestamp, 'mxchat_process_sitemap_urls');
4545 }
4546
4547 // Redirect back with a success message
4548 set_transient('mxchat_admin_notice_success',
4549 esc_html__('Processing has been stopped successfully.', 'mxchat'),
4550 30
4551 );
4552 wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
4553 exit;
4554 }
4555 private function mxchat_sanitize_content_for_api($content) {
4556 // Remove script, style tags, and HTML comments
4557 $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
4558 $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
4559 $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
4560
4561 // Remove all HTML tags and decode HTML entities
4562 $content = wp_strip_all_tags($content);
4563 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
4564
4565 // Trim and normalize whitespace
4566 $content = trim(preg_replace('/\s+/', ' ', $content));
4567
4568 return $content;
4569 }
4570
4571
4572
4573 private function mxchat_fetch_loops_mailing_lists($api_key) {
4574 $url = 'https://app.loops.so/api/v1/lists';
4575 $response = wp_remote_get($url, array(
4576 'headers' => array(
4577 'Authorization' => 'Bearer ' . $api_key,
4578 'Content-Type' => 'application/json'
4579 )
4580 ));
4581
4582 if (is_wp_error($response)) {
4583 return array();
4584 }
4585
4586 $body = wp_remote_retrieve_body($response);
4587 $lists = json_decode($body, true);
4588
4589 return isset($lists) && is_array($lists) ? $lists : array();
4590 }
4591
4592
4593
4594
4595
4596 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
4597 if (empty($vec1) || empty($vec2)) {
4598 return 0.0;
4599 }
4600
4601 $dot_product = 0.0;
4602 $norm_a = 0.0;
4603 $norm_b = 0.0;
4604
4605 for ($i = 0; $i < count($vec1); $i++) {
4606 $dot_product += $vec1[$i] * $vec2[$i];
4607 $norm_a += pow($vec1[$i], 2);
4608 $norm_b += pow($vec2[$i], 2);
4609 }
4610
4611 if ($norm_a == 0.0 || $norm_b == 0.0) {
4612 return 0.0;
4613 } else {
4614 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
4615 }
4616 }
4617
4618
4619
4620
4621 }
4622 ?>
4623