PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.1.9
MxChat – AI Chatbot & Content Generation for WordPress v1.1.9
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-integrator.php

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

780 lines 28.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit;
4 }
5
6 class MxChat_Integrator {
7 private $options;
8 private $chat_count;
9
10 public function __construct() {
11 $this->options = get_option('mxchat_options');
12 $this->chat_count = get_option('mxchat_chat_count', 0);
13
14 // Add WooCommerce hooks
15 add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
16
17 // Ensure embeddings are removed when a product is moved to trash or permanently deleted
18 add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
19 add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
20
21 add_action('wp_enqueue_scripts', array($this, 'mxchat_enqueue_scripts_styles'));
22 add_action('wp_ajax_mxchat_handle_chat_request', array($this, 'mxchat_handle_chat_request'));
23 add_action('wp_ajax_nopriv_mxchat_handle_chat_request', array($this, 'mxchat_handle_chat_request'));
24
25 add_action('wp_ajax_mxchat_dismiss_pre_chat_message', array($this, 'mxchat_dismiss_pre_chat_message'));
26 add_action('wp_ajax_nopriv_mxchat_dismiss_pre_chat_message', array($this, 'mxchat_dismiss_pre_chat_message'));
27 // Add the AJAX actions for checking if the pre-chat message was dismissed
28 add_action('wp_ajax_mxchat_check_pre_chat_message_status', array($this, 'mxchat_check_pre_chat_message_status'));
29 add_action('wp_ajax_nopriv_mxchat_check_pre_chat_message_status', array($this, 'mxchat_check_pre_chat_message_status'));
30
31 add_action('wp_ajax_mxchat_fetch_conversation_history', [$this, 'mxchat_fetch_conversation_history']);
32 add_action('wp_ajax_nopriv_mxchat_fetch_conversation_history', [$this, 'mxchat_fetch_conversation_history']);
33
34
35
36 if (!wp_next_scheduled('mxchat_reset_rate_limits')) {
37 wp_schedule_event(time(), 'daily', 'mxchat_reset_rate_limits');
38 }
39
40 add_action('mxchat_reset_rate_limits', array($this, 'mxchat_reset_rate_limits'));
41 }
42
43 public function mxchat_handle_product_change($post_id, $post, $update) {
44 // Ensure this is a product post type
45 if ($post->post_type !== 'product') {
46 return;
47 }
48
49 // Only generate embeddings if the product is published
50 if ($post->post_status === 'publish') {
51 // Delay the embedding slightly to ensure all product data is available
52 add_action('shutdown', function() use ($post_id) {
53 $product = wc_get_product($post_id);
54 if ($product && $product->get_price() !== '') {
55 $this->mxchat_store_product_embedding($product);
56 } else {
57 // Optionally, log or handle the case where product data is incomplete
58 // error_log("Product {$post_id} does not have complete data. Embedding not generated.");
59 }
60 });
61 }
62 }
63
64 public function mxchat_handle_product_delete($post_id) {
65 if (get_post_type($post_id) !== 'product') {
66 return;
67 }
68
69 global $wpdb;
70 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
71
72 // Delete the embedding associated with this product
73 $wpdb->delete($table_name, array('source_url' => get_permalink($post_id)), array('%s'));
74 }
75
76 private function mxchat_store_product_embedding($product) {
77 if (isset($this->options['enable_woocommerce_integration']) && $this->options['enable_woocommerce_integration'] === '1') {
78
79 $source_url = get_permalink($product->get_id());
80 $regular_price = $product->get_regular_price();
81 $sale_price = $product->get_sale_price();
82 $price = $sale_price ?: $regular_price;
83
84 $description = $product->get_description() . "\n\n" .
85 "Short Description: " . $product->get_short_description() . "\n" .
86 "Price: " . $regular_price . "\n" .
87 "Sale Price: " . ($sale_price ?: 'N/A') . "\n" .
88 "SKU: " . $product->get_sku();
89
90 global $wpdb;
91 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
92
93 // Delete any existing embedding for this product
94 $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));
95
96 // Submit the new content and embedding to the database
97 MxChat_Utils::submit_content_to_db($description, $source_url, $this->options['api_key']);
98 }
99 }
100
101
102
103
104
105 private function mxchat_increment_chat_count() {
106 $chat_count = get_option('mxchat_chat_count', 0);
107 $chat_count++;
108 update_option('mxchat_chat_count', $chat_count);
109 }
110
111 function mxchat_fetch_conversation_history() {
112 if (empty($_POST['session_id'])) {
113 wp_send_json_error(['message' => 'Session ID missing.']);
114 wp_die();
115 }
116
117 $session_id = sanitize_text_field($_POST['session_id']);
118 $history = get_option("mxchat_history_{$session_id}", []); // Retrieve stored history
119
120 if (empty($history)) {
121 wp_send_json_error(['message' => 'No history found.']);
122 wp_die();
123 }
124
125 wp_send_json_success(['conversation' => $history]);
126 wp_die();
127 }
128 private function mxchat_fetch_conversation_history_for_ajax($session_id) {
129 $history = get_option("mxchat_history_{$session_id}", []); // Retrieve stored history based on session ID
130 $formatted_history = [];
131
132 // Format the history to align with the expected structure for OpenAI
133 foreach ($history as $entry) {
134 $formatted_history[] = [
135 'role' => $entry['role'], // Ensure this matches 'user' or 'assistant'
136 'content' => $entry['content']
137 ];
138 }
139
140 return $formatted_history;
141 }
142
143
144 private function mxchat_save_chat_message($session_id, $role, $message) {
145 global $wpdb;
146 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
147
148 $user_id = is_user_logged_in() ? get_current_user_id() : 0;
149 $user_identifier = MxChat_User::mxchat_get_user_identifier();
150 $user_email = MxChat_User::mxchat_get_user_email();
151
152 $history = get_option("mxchat_history_{$session_id}", []);
153 $history[] = ['role' => $role, 'content' => $message];
154 update_option("mxchat_history_{$session_id}", $history);
155
156 $wpdb->insert($table_name, [
157 'user_id' => $user_id,
158 'user_identifier' => $user_identifier,
159 'user_email' => $user_email,
160 'session_id' => $session_id,
161 'role' => $role,
162 'message' => $message,
163 'timestamp' => current_time('mysql', 1)
164 ]);
165 }
166
167
168 public function mxchat_handle_chat_request() {
169 global $wpdb;
170
171 // Get and sanitize the user identifier
172 $user_id = $this->mxchat_get_user_identifier();
173 $user_id = sanitize_key($user_id);
174
175 // Setup rate limiting
176 $rate_limit_transient_key = 'mxchat_chat_limit_' . $user_id;
177 $chat_count = get_transient($rate_limit_transient_key) ?: 0;
178
179 // Retrieve the session ID from the client's POST data
180 $session_id = isset($_POST['session_id']) ? sanitize_text_field($_POST['session_id']) : '';
181
182 if (empty($session_id)) {
183 // Handle the case where the session ID is missing
184 wp_send_json_error('Session ID is missing.');
185 wp_die();
186 }
187
188 // No need to set transients or server-side cookies for the session ID
189
190 // Check rate limit
191 $rate_limit_option = $this->options['rate_limit'] ?? 'unlimited';
192 if ($rate_limit_option !== 'unlimited' && $chat_count >= intval($rate_limit_option)) {
193 wp_send_json_error(['message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.']);
194 wp_die();
195 }
196 set_transient($rate_limit_transient_key, $chat_count + 1, DAY_IN_SECONDS);
197
198 // Validate and sanitize the incoming message
199 if (empty($_POST['message'])) {
200 wp_send_json_error('No message received');
201 wp_die();
202 }
203
204 $message = sanitize_text_field($_POST['message']);
205 $this->mxchat_save_chat_message($session_id, 'user', $message);
206
207 // Track email capture and WooCommerce flows with individual transients
208 $email_capture_prompt = get_transient('mxchat_email_capture_' . $user_id);
209 $interaction_count = get_transient('mxchat_email_interaction_count_' . $user_id) ?: 0;
210 $woocommerce_prompt = get_transient('mxchat_woocommerce_prompt_' . $user_id);
211
212 // Handle email capture flow
213 if ($email_capture_prompt) {
214 if (preg_match('/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/i', $message, $matches)) {
215 $email = $matches[0];
216 $this->add_email_to_loops($email);
217 $response = $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.';
218
219 delete_transient('mxchat_email_capture_' . $user_id);
220 delete_transient('mxchat_email_interaction_count_' . $user_id);
221 delete_transient('mxchat_woocommerce_prompt_' . $user_id);
222
223 $this->mxchat_save_chat_message($session_id, 'bot', $response);
224 wp_send_json(['message' => $response]);
225 wp_die();
226 } else {
227 if ($interaction_count >= 3) {
228 delete_transient('mxchat_email_capture_' . $user_id);
229 delete_transient('mxchat_email_interaction_count_' . $user_id);
230 } else {
231 set_transient('mxchat_email_interaction_count_' . $user_id, ++$interaction_count, 5 * MINUTE_IN_SECONDS);
232 }
233 }
234 }
235
236 // Handle WooCommerce add-to-cart flow
237 if (class_exists('WooCommerce') && stripos($message, 'add to cart') !== false) {
238 $last_product_id = get_transient('mxchat_last_discussed_product_' . $user_id);
239 if ($last_product_id) {
240 $added = WC()->cart->add_to_cart($last_product_id);
241 $product = wc_get_product($last_product_id);
242
243 if ($added) {
244 $response = "The product '{$product->get_name()}' has been added to your cart. To proceed to checkout, please type 'checkout'.";
245 set_transient('mxchat_checkout_prompt_' . $user_id, true, 5 * MINUTE_IN_SECONDS);
246 $this->mxchat_save_chat_message($session_id, 'bot', $response);
247 wp_send_json(['message' => $response]);
248 wp_die();
249 } else {
250 $response = "Sorry, I couldn't add the product to your cart. Please try again.";
251 $this->mxchat_save_chat_message($session_id, 'bot', $response);
252 wp_send_json(['message' => $response]);
253 wp_die();
254 }
255 } else {
256 $response = "I couldn't find the product to add. Please mention the product name again.";
257 $this->mxchat_save_chat_message($session_id, 'bot', $response);
258 wp_send_json(['message' => $response]);
259 wp_die();
260 }
261 }
262
263 // Handle checkout response
264 if (class_exists('WooCommerce') && stripos($message, 'checkout') !== false) {
265 $checkout_prompt = get_transient('mxchat_checkout_prompt_' . $user_id);
266 if ($checkout_prompt && WC()->cart->get_cart_contents_count() > 0) {
267 $checkout_url = wc_get_checkout_url();
268 $response = "Great! Redirecting you to the checkout page...";
269 delete_transient('mxchat_checkout_prompt_' . $user_id);
270
271 $this->mxchat_save_chat_message($session_id, 'bot', $response);
272 wp_send_json(['message' => $response, 'redirect_url' => $checkout_url]);
273 wp_die();
274 } else {
275 $response = "It seems like there is no active checkout prompt or no items in your cart. Please add a product to the cart first.";
276 $this->mxchat_save_chat_message($session_id, 'bot', $response);
277 wp_send_json(['message' => $response]);
278 wp_die();
279 }
280 }
281
282 // Handle order-related queries
283 if (class_exists('WooCommerce') && MxChat_WooCommerce::mxchat_is_order_related_query($message)) {
284 $response = MxChat_WooCommerce::mxchat_fetch_user_orders_details();
285 $this->mxchat_save_chat_message($session_id, 'bot', $response);
286 wp_send_json(['message' => $response]);
287 wp_die();
288 }
289
290 // Check for trigger keywords to initiate email capture
291 $trigger_keywords = explode(',', $this->options['trigger_keywords'] ?? '');
292 if (!empty($trigger_keywords) && $trigger_keywords[0] !== '') {
293 foreach ($trigger_keywords as $keyword) {
294 if (stripos($message, trim($keyword)) !== false) {
295 $response = $this->options['triggered_phrase_response'] ?? "Would you like to join our mailing list? Please provide your email below.";
296 set_transient('mxchat_email_capture_' . $user_id, true, 5 * MINUTE_IN_SECONDS);
297 $this->mxchat_save_chat_message($session_id, 'bot', $response);
298 wp_send_json(['message' => $response]);
299 wp_die();
300 }
301 }
302 }
303
304 // Store product discussion in transient
305 $last_discussed_product_id = MxChat_WooCommerce::mxchat_extract_product_id_from_message($message);
306 if ($last_discussed_product_id) {
307 set_transient('mxchat_last_discussed_product_' . $user_id, $last_discussed_product_id, 3600);
308 }
309
310 // Standard chat processing
311 $user_message_embedding = $this->mxchat_generate_embedding($message, $this->options['api_key']);
312 if (!is_array($user_message_embedding)) {
313 wp_send_json_error('Error processing your message.');
314 wp_die();
315 }
316
317 $relevant_content = $this->mxchat_find_relevant_content($user_message_embedding);
318 $conversation_history = $this->mxchat_fetch_conversation_history_for_ajax($session_id);
319 $this->mxchat_increment_chat_count();
320 $response = $this->mxchat_generate_response(
321 $relevant_content,
322 $this->options['api_key'], // OpenAI API Key
323 $this->options['xai_api_key'], // X.AI API Key
324 $this->options['claude_api_key'], // Claude API Key
325 $conversation_history
326 );
327
328 $this->mxchat_save_chat_message($session_id, 'bot', $response);
329 wp_send_json(['message' => $response, 'session_id' => $session_id]);
330 wp_die();
331 }
332
333
334 // Function to add the captured email to Loops
335 private function add_email_to_loops($email) {
336 $api_key = $this->options['loops_api_key'];
337 $mailing_list_id = $this->options['loops_mailing_list'];
338
339 $data = array(
340 'email' => $email,
341 'subscribed' => true,
342 'source' => 'MxChat AI Chatbot',
343 'mailingLists' => array($mailing_list_id => true),
344 );
345
346 $url = "https://app.loops.so/api/v1/contacts/create";
347 $args = array(
348 'body' => json_encode($data),
349 'headers' => array(
350 'Authorization' => 'Bearer ' . $api_key,
351 'Content-Type' => 'application/json',
352 ),
353 'method' => 'POST',
354 'timeout' => 45,
355 );
356
357 wp_remote_post($url, $args);
358 }
359
360
361 private function mxchat_get_user_identifier() {
362 return MxChat_User::mxchat_get_user_identifier();
363 }
364
365
366
367 private function mxchat_generate_embedding($text, $api_key) {
368 $endpoint = 'https://api.openai.com/v1/embeddings';
369
370 $body = wp_json_encode([
371 'input' => $text,
372 'model' => 'text-embedding-ada-002'
373 ]);
374
375 $args = [
376 'body' => $body,
377 'headers' => [
378 'Content-Type' => 'application/json',
379 'Authorization' => 'Bearer ' . $api_key,
380 ],
381 'timeout' => 60,
382 'redirection' => 5,
383 'blocking' => true,
384 'httpversion' => '1.0',
385 'sslverify' => true,
386 ];
387
388 $response = wp_remote_post($endpoint, $args);
389
390 if (is_wp_error($response)) {
391 return null;
392 }
393
394 $response_body = json_decode(wp_remote_retrieve_body($response), true);
395
396 if (isset($response_body['data'][0]['embedding']) && is_array($response_body['data'][0]['embedding'])) {
397 return $response_body['data'][0]['embedding'];
398 } else {
399 return null;
400 }
401 }
402
403 private function mxchat_find_relevant_content($user_embedding) {
404 global $wpdb;
405 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
406
407 // Define a cache key for embeddings
408 $cache_key = 'mxchat_system_prompt_embeddings';
409
410 // Attempt to get the embeddings from the cache
411 $embeddings = wp_cache_get($cache_key, 'mxchat_system_prompts');
412
413 if ($embeddings === false) {
414 // Cache miss, query the database and cache the results
415 $query = "SELECT id, embedding_vector FROM {$system_prompt_table}";
416 $embeddings = $wpdb->get_results($query);
417
418 if ($embeddings === null || empty($embeddings)) {
419 //error_log("No embeddings found in the database.");
420 return null; // Return null to handle no embeddings gracefully
421 }
422
423 // Cache the results if successful
424 wp_cache_set($cache_key, $embeddings, 'mxchat_system_prompts', 3600); // Cache for 1 hour
425 }
426
427 $most_relevant_id = null;
428 $highest_similarity = -INF;
429
430 foreach ($embeddings as $embedding) {
431 $database_embedding = maybe_unserialize($embedding->embedding_vector);
432
433 // Debugging: Log the embeddings
434 // if (!is_array($database_embedding)) {
435 // error_log("Invalid database embedding format for ID {$embedding->id}: " . print_r($database_embedding, true));
436 // continue;
437 // }
438
439 if (is_array($user_embedding)) {
440 $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $database_embedding);
441
442 // Debugging: Log the similarity score
443 // error_log("Calculated similarity for ID {$embedding->id}: {$similarity}");
444
445 if ($similarity > $highest_similarity) {
446 $highest_similarity = $similarity;
447 $most_relevant_id = $embedding->id;
448 }
449 } else {
450 // error_log("User embedding is not an array. Embedding data: " . print_r($user_embedding, true));
451 }
452 }
453
454 if ($most_relevant_id !== null) {
455 // Fetch content with product links
456 return $this->fetch_content_with_product_links($most_relevant_id);
457 }
458
459 //error_log("No relevant content found. Most relevant ID was null.");
460 return null; // Return null if no relevant content is found
461 }
462
463
464 private function fetch_content_with_product_links($most_relevant_id) {
465 global $wpdb;
466 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
467
468 // Fetch the article content and associated product URL
469 $query = $wpdb->prepare("SELECT article_content, source_url FROM {$system_prompt_table} WHERE id = %d", $most_relevant_id);
470 $result = $wpdb->get_row($query);
471
472 if ($result) {
473 // Append the product link to the content if available
474 $content = $result->article_content;
475 if (!empty($result->source_url)) {
476 $content .= "\n\nFor more details, check out this product: " . esc_url($result->source_url);
477 }
478 return $content;
479 }
480
481 return null;
482 }
483
484 private function mxchat_generate_response($relevant_content, $api_key, $xai_api_key, $claude_api_key, $conversation_history) {
485 if (!$relevant_content) {
486 return "I'm sorry, I couldn't find relevant information on that topic.";
487 }
488
489 // Ensure consistency: Replace 'bot' role with 'assistant' in conversation history
490 foreach ($conversation_history as &$message) {
491 if ($message['role'] === 'bot') {
492 $message['role'] = 'assistant';
493 }
494 }
495
496 // Check the selected model
497 $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo';
498
499 // Variables for API URL and API key
500 $api_url = '';
501 $selected_api_key = '';
502 $headers = [];
503
504 // Decide whether to use OpenAI, X.AI, or Claude based on the selected model
505 if ($selected_model === 'grok-beta') {
506 // X.AI Model selected
507 $api_url = 'https://api.x.ai/v1/chat/completions'; // X.AI endpoint
508 $selected_api_key = $xai_api_key; // Use X.AI API key
509 $headers = [
510 'Content-Type' => 'application/json',
511 'Authorization' => 'Bearer ' . $selected_api_key, // X.AI API key in Authorization header
512 ];
513 $request_body = [
514 'model' => $selected_model,
515 'messages' => $conversation_history,
516 'temperature' => 0.8,
517 'stream' => false
518 ];
519
520 } elseif (strpos($selected_model, 'claude') !== false) {
521 // Claude model selected
522 $api_url = 'https://api.anthropic.com/v1/messages';
523 $selected_api_key = $claude_api_key;
524
525 // Add correct headers for Claude API
526 $headers = [
527 'Content-Type' => 'application/json',
528 'x-api-key' => $selected_api_key,
529 'anthropic-version' => '2023-06-01'
530 ];
531
532 // The system prompt should be passed only at the beginning of the conversation
533 $system_prompt = "You are an AI Chatbot assistant for MxChat.AI. Your role is to answer questions about the MxChat WordPress plugin and its features. Provide concise and helpful responses.";
534
535 // If conversation history is empty, add the system prompt
536 if (empty($conversation_history)) {
537 $conversation_history[] = [
538 'role' => 'system',
539 'content' => $system_prompt
540 ];
541 }
542
543 // Add the new user message to the conversation
544 $conversation_history[] = [
545 'role' => 'user',
546 'content' => $relevant_content
547 ];
548
549 // Construct the request body for Claude
550 $request_body = [
551 'model' => $selected_model,
552 'max_tokens' => 1000,
553 'temperature' => 0.8,
554 'messages' => $conversation_history
555 ];
556
557 } else {
558 // OpenAI Model selected
559 $api_url = 'https://api.openai.com/v1/chat/completions'; // OpenAI endpoint
560 $selected_api_key = $api_key; // Use OpenAI API key
561 $headers = [
562 'Content-Type' => 'application/json',
563 'Authorization' => 'Bearer ' . $selected_api_key, // OpenAI API key in Authorization header
564 ];
565 $request_body = [
566 'model' => $selected_model,
567 'messages' => $conversation_history,
568 'temperature' => 0.8,
569 'stream' => false
570 ];
571 }
572
573 // Build the body for the API request
574 $body = json_encode($request_body);
575
576 // Set up the arguments for the API request
577 $args = [
578 'body' => $body,
579 'headers' => $headers, // Use the headers for the selected API
580 'timeout' => 60,
581 'redirection' => 5,
582 'blocking' => true,
583 'httpversion' => '1.0',
584 'sslverify' => true,
585 ];
586
587 // Debug log for request body and headers
588 //error_log("Request Body: " . $body);
589 //error_log("API Args (headers): " . print_r($headers, true));
590
591 // Make the API request
592 $response = wp_remote_post($api_url, $args);
593
594 // Check if the request failed
595 if (is_wp_error($response)) {
596 //error_log("API request failed: " . $response->get_error_message());
597 return "Sorry, there was an error processing your request.";
598 }
599
600 // Decode the response
601 $response_body = wp_remote_retrieve_body($response);
602 $decoded_response = json_decode($response_body, true);
603
604 // Debug log for response
605 //error_log("API Response: " . print_r($decoded_response, true));
606
607 // Handle the response depending on which API was used
608 if (isset($decoded_response['choices'][0]['message']['content'])) {
609 // OpenAI/X.AI case: extract the content as usual
610 return trim($decoded_response['choices'][0]['message']['content']);
611 } elseif (isset($decoded_response['content'][0]['text'])) {
612 // Claude-specific: extract text from the 'content' field
613 return trim($decoded_response['content'][0]['text']);
614 } else {
615 return "Sorry, I couldn't process that request.";
616 }
617 }
618
619 public function mxchat_dismiss_pre_chat_message() {
620 // Get and sanitize the user identifier
621 $user_id = $this->mxchat_get_user_identifier();
622 $user_id = sanitize_key($user_id);
623
624 // Set a transient to track that the user has dismissed the pre-chat message
625 $transient_key = 'mxchat_pre_chat_message_dismissed_' . $user_id;
626 set_transient($transient_key, true, DAY_IN_SECONDS);
627
628 wp_send_json_success();
629 }
630
631 public function mxchat_check_pre_chat_message_status() {
632 // Get and sanitize the user identifier
633 $user_id = $this->mxchat_get_user_identifier();
634 $user_id = sanitize_key($user_id);
635
636 // Check if the transient exists (i.e., if the message was dismissed)
637 $transient_key = 'mxchat_pre_chat_message_dismissed_' . $user_id;
638 $dismissed = get_transient($transient_key);
639
640 // Log the result to see if it's being set correctly
641 //error_log("Check pre-chat message dismissed for $user_id: " . ($dismissed ? 'Yes' : 'No'));
642
643 if ($dismissed) {
644 wp_send_json_success(['dismissed' => true]);
645 } else {
646 wp_send_json_success(['dismissed' => false]);
647 }
648
649 wp_die();
650 }
651
652
653
654
655
656 private function mxchat_calculate_cosine_similarity($vectorA, $vectorB) {
657 if (!is_array($vectorA) || !is_array($vectorB) || empty($vectorA) || empty($vectorB)) {
658 return 0;
659 }
660
661 $dotProduct = array_sum(array_map(function ($a, $b) {
662 return $a * $b;
663 }, $vectorA, $vectorB));
664 $normA = sqrt(array_sum(array_map(function ($a) {
665 return $a * $a;
666 }, $vectorA)));
667 $normB = sqrt(array_sum(array_map(function ($b) {
668 return $b * $b;
669 }, $vectorB)));
670
671 if ($normA == 0 || $normB == 0) {
672 return 0;
673 }
674
675 return $dotProduct / ($normA * $normB);
676 }
677
678 public function mxchat_enqueue_scripts_styles() {
679 // Define version numbers for the styles and scripts
680 $chat_style_version = '1.1.9'; // Replace with your actual version
681 $chat_script_version = '1.1.9'; // Replace with your actual version
682
683 // Correct path to the script file
684 wp_enqueue_script(
685 'mxchat-chat-js', // Handle for the script
686 plugin_dir_url(__FILE__) . '../js/chat-script.js', // Correct path using __FILE__
687 array('jquery'), // Dependencies
688 $chat_script_version, // Version for cache busting
689 true // Load script in footer
690 );
691
692 // Enqueue the CSS file similarly
693 wp_enqueue_style(
694 'mxchat-chat-css', // Handle for the style
695 plugin_dir_url(__FILE__) . '../css/chat-style.css', // Correct path using __FILE__
696 array(), // No dependencies
697 $chat_style_version // Version for cache busting
698 );
699
700 // Fetch options from the database
701 $this->options = get_option('mxchat_options');
702
703 // Prepare settings to pass to JavaScript
704 $style_settings = array(
705 'ajax_url' => admin_url('admin-ajax.php'),
706 'nonce' => wp_create_nonce('mxchat_chat_nonce'), // Nonce for security
707 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
708 'appendWidgetToBody' => $this->options['append_to_body'] ?? 'off'
709 );
710
711 // Localize the script with necessary data
712 wp_localize_script('mxchat-chat-js', 'mxchatChat', $style_settings);
713 }
714
715
716
717 public function mxchat_reset_rate_limits() {
718 global $wpdb;
719
720 // Define a cache key pattern for rate limits
721 $cache_key_pattern = 'mxchat_chat_limit_%';
722
723 // Retrieve all option names matching the pattern
724 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
725 $option_names = $wpdb->get_col("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE 'mxchat_chat_limit_%'");
726
727 // db call ok; no-cache ok
728 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- db call ok
729 $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE 'mxchat_chat_limit_%'");
730
731 // Clear the relevant cache entries
732 foreach ($option_names as $option_name) {
733 wp_cache_delete($option_name, 'options');
734 }
735
736 // Optionally, clear a general cache if you have one
737 wp_cache_delete('mxchat_all_chat_limits', 'options');
738 }
739
740
741 private function mxchat_fetch_woocommerce_products() {
742 // Ensure WooCommerce is active
743 if (!class_exists('WooCommerce')) {
744 return [];
745 }
746
747 $args = array(
748 'post_type' => 'product',
749 'post_status' => 'publish',
750 'posts_per_page' => -1,
751 );
752
753 $products = get_posts($args);
754 $product_data = [];
755
756 foreach ($products as $product) {
757 $product_id = $product->ID;
758 $product_obj = wc_get_product($product_id);
759
760 $product_data[] = array(
761 'id' => $product_id,
762 'name' => $product_obj->get_name(),
763 'description' => $product_obj->get_description(),
764 'short_description' => $product_obj->get_short_description(),
765 'url' => get_permalink($product_id),
766 'price' => $product_obj->get_regular_price(),
767 'sale_price' => $product_obj->get_sale_price(),
768 'stock_status' => $product_obj->get_stock_status(),
769 'sku' => $product_obj->get_sku(),
770 'in_stock' => $product_obj->is_in_stock(),
771 'total_sales' => $product_obj->get_total_sales(),
772 );
773 }
774
775 return $product_data;
776 }
777
778 }
779 ?>
780