PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.1.8
MxChat – AI Chatbot & Content Generation for WordPress v1.1.8
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.8, at includes/class-mxchat-integrator.php

729 lines 27.1 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($relevant_content, $this->options['api_key'], $this->options['xai_api_key'], $conversation_history);
321
322 $this->mxchat_save_chat_message($session_id, 'bot', $response);
323 wp_send_json(['message' => $response, 'session_id' => $session_id]);
324 wp_die();
325 }
326
327
328 // Function to add the captured email to Loops
329 private function add_email_to_loops($email) {
330 $api_key = $this->options['loops_api_key'];
331 $mailing_list_id = $this->options['loops_mailing_list'];
332
333 $data = array(
334 'email' => $email,
335 'subscribed' => true,
336 'source' => 'MxChat AI Chatbot',
337 'mailingLists' => array($mailing_list_id => true),
338 );
339
340 $url = "https://app.loops.so/api/v1/contacts/create";
341 $args = array(
342 'body' => json_encode($data),
343 'headers' => array(
344 'Authorization' => 'Bearer ' . $api_key,
345 'Content-Type' => 'application/json',
346 ),
347 'method' => 'POST',
348 'timeout' => 45,
349 );
350
351 wp_remote_post($url, $args);
352 }
353
354
355 private function mxchat_get_user_identifier() {
356 return MxChat_User::mxchat_get_user_identifier();
357 }
358
359
360
361 private function mxchat_generate_embedding($text, $api_key) {
362 $endpoint = 'https://api.openai.com/v1/embeddings';
363
364 $body = wp_json_encode([
365 'input' => $text,
366 'model' => 'text-embedding-ada-002'
367 ]);
368
369 $args = [
370 'body' => $body,
371 'headers' => [
372 'Content-Type' => 'application/json',
373 'Authorization' => 'Bearer ' . $api_key,
374 ],
375 'timeout' => 60,
376 'redirection' => 5,
377 'blocking' => true,
378 'httpversion' => '1.0',
379 'sslverify' => true,
380 ];
381
382 $response = wp_remote_post($endpoint, $args);
383
384 if (is_wp_error($response)) {
385 return null;
386 }
387
388 $response_body = json_decode(wp_remote_retrieve_body($response), true);
389
390 if (isset($response_body['data'][0]['embedding']) && is_array($response_body['data'][0]['embedding'])) {
391 return $response_body['data'][0]['embedding'];
392 } else {
393 return null;
394 }
395 }
396
397 private function mxchat_find_relevant_content($user_embedding) {
398 global $wpdb;
399 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
400
401 // Define a cache key for embeddings
402 $cache_key = 'mxchat_system_prompt_embeddings';
403
404 // Attempt to get the embeddings from the cache
405 $embeddings = wp_cache_get($cache_key, 'mxchat_system_prompts');
406
407 if ($embeddings === false) {
408 // Cache miss, query the database and cache the results
409 $query = "SELECT id, embedding_vector FROM {$system_prompt_table}";
410 $embeddings = $wpdb->get_results($query);
411
412 if ($embeddings === null || empty($embeddings)) {
413 //error_log("No embeddings found in the database.");
414 return null; // Return null to handle no embeddings gracefully
415 }
416
417 // Cache the results if successful
418 wp_cache_set($cache_key, $embeddings, 'mxchat_system_prompts', 3600); // Cache for 1 hour
419 }
420
421 $most_relevant_id = null;
422 $highest_similarity = -INF;
423
424 foreach ($embeddings as $embedding) {
425 $database_embedding = maybe_unserialize($embedding->embedding_vector);
426
427 // Debugging: Log the embeddings
428 // if (!is_array($database_embedding)) {
429 // error_log("Invalid database embedding format for ID {$embedding->id}: " . print_r($database_embedding, true));
430 // continue;
431 // }
432
433 if (is_array($user_embedding)) {
434 $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $database_embedding);
435
436 // Debugging: Log the similarity score
437 // error_log("Calculated similarity for ID {$embedding->id}: {$similarity}");
438
439 if ($similarity > $highest_similarity) {
440 $highest_similarity = $similarity;
441 $most_relevant_id = $embedding->id;
442 }
443 } else {
444 // error_log("User embedding is not an array. Embedding data: " . print_r($user_embedding, true));
445 }
446 }
447
448 if ($most_relevant_id !== null) {
449 // Fetch content with product links
450 return $this->fetch_content_with_product_links($most_relevant_id);
451 }
452
453 //error_log("No relevant content found. Most relevant ID was null.");
454 return null; // Return null if no relevant content is found
455 }
456
457
458 private function fetch_content_with_product_links($most_relevant_id) {
459 global $wpdb;
460 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
461
462 // Fetch the article content and associated product URL
463 $query = $wpdb->prepare("SELECT article_content, source_url FROM {$system_prompt_table} WHERE id = %d", $most_relevant_id);
464 $result = $wpdb->get_row($query);
465
466 if ($result) {
467 // Append the product link to the content if available
468 $content = $result->article_content;
469 if (!empty($result->source_url)) {
470 $content .= "\n\nFor more details, check out this product: " . esc_url($result->source_url);
471 }
472 return $content;
473 }
474
475 return null;
476 }
477
478 private function mxchat_generate_response($relevant_content, $api_key, $xai_api_key, $conversation_history) {
479 if (!$relevant_content) {
480 return "I'm sorry, I couldn't find relevant information on that topic.";
481 }
482
483 // Get system prompt instructions from options
484 $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';
485
486 // Add system prompt to relevant content
487 $content_with_instructions = $system_prompt_instructions . " " . $relevant_content;
488
489 // Prepend system instructions to the conversation history
490 array_unshift($conversation_history, [
491 'role' => 'system',
492 'content' => "Here are your instructions: " . $content_with_instructions
493 ]);
494
495 // Ensure consistency: Replace 'bot' role with 'assistant' in conversation history
496 foreach ($conversation_history as &$message) {
497 if ($message['role'] === 'bot') {
498 $message['role'] = 'assistant';
499 }
500 }
501
502 // Check the selected model
503 $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo';
504
505 // Variables for API URL and API key
506 $api_url = '';
507 $selected_api_key = '';
508
509 // Decide whether to use OpenAI or X.AI based on the selected model
510 if ($selected_model === 'grok-beta') {
511 // X.AI Model selected
512 $api_url = 'https://api.x.ai/v1/chat/completions'; // X.AI endpoint
513 $selected_api_key = $xai_api_key; // Use X.AI API key
514 } else {
515 // OpenAI model selected
516 $api_url = 'https://api.openai.com/v1/chat/completions'; // OpenAI endpoint
517 $selected_api_key = $api_key; // Use OpenAI API key
518 }
519
520 // Build the body for the API request
521 $body = json_encode([
522 'model' => $selected_model, // Use the selected model
523 'messages' => $conversation_history,
524 'temperature' => 0.8, // Adjust as per your needs
525 'stream' => false // Example option, adjust as needed
526 ]);
527
528 // Set up the arguments for the API request
529 $args = [
530 'body' => $body,
531 'headers' => [
532 'Content-Type' => 'application/json',
533 'Authorization' => 'Bearer ' . $selected_api_key, // Use the selected API key
534 ],
535 'timeout' => 60,
536 'redirection' => 5,
537 'blocking' => true,
538 'httpversion' => '1.0',
539 'sslverify' => true,
540 ];
541
542 // Make the API request
543 $response = wp_remote_post($api_url, $args);
544
545 // Check if the request failed
546 if (is_wp_error($response)) {
547 return "Sorry, there was an error processing your request.";
548 }
549
550 // Decode the response
551 $response_body = json_decode(wp_remote_retrieve_body($response), true);
552
553 // Check if the response contains valid content
554 if (isset($response_body['choices'][0]['message']['content'])) {
555 // Optionally handle token usage for cost tracking
556 if (isset($response_body['usage'])) {
557 $prompt_tokens = $response_body['usage']['prompt_tokens'];
558 $total_tokens = $response_body['usage']['total_tokens'];
559 }
560 // Return the assistant's response
561 return trim($response_body['choices'][0]['message']['content']);
562 } else {
563 return "Sorry, I couldn't process that request.";
564 }
565 }
566
567
568 public function mxchat_dismiss_pre_chat_message() {
569 // Get and sanitize the user identifier
570 $user_id = $this->mxchat_get_user_identifier();
571 $user_id = sanitize_key($user_id);
572
573 // Set a transient to track that the user has dismissed the pre-chat message
574 $transient_key = 'mxchat_pre_chat_message_dismissed_' . $user_id;
575 set_transient($transient_key, true, DAY_IN_SECONDS);
576
577 wp_send_json_success();
578 }
579
580 public function mxchat_check_pre_chat_message_status() {
581 // Get and sanitize the user identifier
582 $user_id = $this->mxchat_get_user_identifier();
583 $user_id = sanitize_key($user_id);
584
585 // Check if the transient exists (i.e., if the message was dismissed)
586 $transient_key = 'mxchat_pre_chat_message_dismissed_' . $user_id;
587 $dismissed = get_transient($transient_key);
588
589 // Log the result to see if it's being set correctly
590 //error_log("Check pre-chat message dismissed for $user_id: " . ($dismissed ? 'Yes' : 'No'));
591
592 if ($dismissed) {
593 wp_send_json_success(['dismissed' => true]);
594 } else {
595 wp_send_json_success(['dismissed' => false]);
596 }
597
598 wp_die();
599 }
600
601
602
603
604
605 private function mxchat_calculate_cosine_similarity($vectorA, $vectorB) {
606 if (!is_array($vectorA) || !is_array($vectorB) || empty($vectorA) || empty($vectorB)) {
607 return 0;
608 }
609
610 $dotProduct = array_sum(array_map(function ($a, $b) {
611 return $a * $b;
612 }, $vectorA, $vectorB));
613 $normA = sqrt(array_sum(array_map(function ($a) {
614 return $a * $a;
615 }, $vectorA)));
616 $normB = sqrt(array_sum(array_map(function ($b) {
617 return $b * $b;
618 }, $vectorB)));
619
620 if ($normA == 0 || $normB == 0) {
621 return 0;
622 }
623
624 return $dotProduct / ($normA * $normB);
625 }
626
627 public function mxchat_enqueue_scripts_styles() {
628 // Define version numbers for the styles and scripts
629 $chat_style_version = '1.1.8'; // Replace with your actual version
630 $chat_script_version = '1.1.8'; // Replace with your actual version
631
632 // Correct path to the script file
633 wp_enqueue_script(
634 'mxchat-chat-js', // Handle for the script
635 plugin_dir_url(__FILE__) . '../js/chat-script.js', // Correct path using __FILE__
636 array('jquery'), // Dependencies
637 $chat_script_version, // Version for cache busting
638 true // Load script in footer
639 );
640
641 // Enqueue the CSS file similarly
642 wp_enqueue_style(
643 'mxchat-chat-css', // Handle for the style
644 plugin_dir_url(__FILE__) . '../css/chat-style.css', // Correct path using __FILE__
645 array(), // No dependencies
646 $chat_style_version // Version for cache busting
647 );
648
649 // Fetch options from the database
650 $this->options = get_option('mxchat_options');
651
652 // Prepare settings to pass to JavaScript
653 $style_settings = array(
654 'ajax_url' => admin_url('admin-ajax.php'),
655 'nonce' => wp_create_nonce('mxchat_chat_nonce'), // Nonce for security
656 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
657 'appendWidgetToBody' => $this->options['append_to_body'] ?? 'off'
658 );
659
660 // Localize the script with necessary data
661 wp_localize_script('mxchat-chat-js', 'mxchatChat', $style_settings);
662 }
663
664
665
666 public function mxchat_reset_rate_limits() {
667 global $wpdb;
668
669 // Define a cache key pattern for rate limits
670 $cache_key_pattern = 'mxchat_chat_limit_%';
671
672 // Retrieve all option names matching the pattern
673 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
674 $option_names = $wpdb->get_col("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE 'mxchat_chat_limit_%'");
675
676 // db call ok; no-cache ok
677 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- db call ok
678 $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE 'mxchat_chat_limit_%'");
679
680 // Clear the relevant cache entries
681 foreach ($option_names as $option_name) {
682 wp_cache_delete($option_name, 'options');
683 }
684
685 // Optionally, clear a general cache if you have one
686 wp_cache_delete('mxchat_all_chat_limits', 'options');
687 }
688
689
690 private function mxchat_fetch_woocommerce_products() {
691 // Ensure WooCommerce is active
692 if (!class_exists('WooCommerce')) {
693 return [];
694 }
695
696 $args = array(
697 'post_type' => 'product',
698 'post_status' => 'publish',
699 'posts_per_page' => -1,
700 );
701
702 $products = get_posts($args);
703 $product_data = [];
704
705 foreach ($products as $product) {
706 $product_id = $product->ID;
707 $product_obj = wc_get_product($product_id);
708
709 $product_data[] = array(
710 'id' => $product_id,
711 'name' => $product_obj->get_name(),
712 'description' => $product_obj->get_description(),
713 'short_description' => $product_obj->get_short_description(),
714 'url' => get_permalink($product_id),
715 'price' => $product_obj->get_regular_price(),
716 'sale_price' => $product_obj->get_sale_price(),
717 'stock_status' => $product_obj->get_stock_status(),
718 'sku' => $product_obj->get_sku(),
719 'in_stock' => $product_obj->is_in_stock(),
720 'total_sales' => $product_obj->get_total_sales(),
721 );
722 }
723
724 return $product_data;
725 }
726
727 }
728 ?>
729