PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.4.0
MxChat – AI Chatbot & Content Generation for WordPress v2.4.0
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 / mxchat-basic.php

mxchat-basic.php in MxChat – AI Chatbot & Content Generation for WordPress 2.4.0, at mxchat-basic.php

634 lines 22.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: MxChat
4 * Description: AI chatbot for WordPress with OpenAI, Claude, xAI, DeepSeek, live agent, PDF uploads, WooCommerce, and training on website data.
5 * Version: 2.4.0
6 * Author: MxChat
7 * Author URI: https://mxchat.ai
8 * License: GPLv2 or later
9 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
10 * Text Domain: mxchat
11 * Domain Path: /languages
12 */
13
14 if (!defined('ABSPATH')) {
15 exit; // Exit if accessed directly.
16 }
17
18 // Define plugin version constant for asset versioning
19 define('MXCHAT_VERSION', '2.4.0');
20
21 function mxchat_load_textdomain() {
22 load_plugin_textdomain('mxchat', false, dirname(plugin_basename(__FILE__)) . '/languages');
23 }
24 add_action('plugins_loaded', 'mxchat_load_textdomain');
25
26 // Include classes with error handling
27 function mxchat_include_classes() {
28 $class_files = array(
29 'includes/class-mxchat-integrator.php',
30 'includes/class-mxchat-admin.php',
31 'includes/class-mxchat-public.php',
32 'includes/class-mxchat-utils.php',
33 'includes/class-mxchat-user.php',
34 'includes/class-mxchat-meta-box.php',
35 'includes/pdf-parser/alt_autoload.php',
36 'includes/class-mxchat-word-handler.php',
37 'admin/class-ajax-handler.php',
38 'admin/class-pinecone-manager.php',
39 'admin/class-knowledge-manager.php'
40 );
41
42 foreach ($class_files as $file) {
43 $file_path = plugin_dir_path(__FILE__) . $file;
44 if (file_exists($file_path)) {
45 require_once $file_path;
46 } else {
47 //error_log('MxChat: Missing class file - ' . $file);
48 }
49 }
50 }
51
52 /**
53 * Create URL click tracking table
54 */
55 function mxchat_create_url_clicks_table() {
56 global $wpdb;
57
58 $table_name = $wpdb->prefix . 'mxchat_url_clicks';
59
60 $charset_collate = $wpdb->get_charset_collate();
61
62 $sql = "CREATE TABLE $table_name (
63 id mediumint(9) NOT NULL AUTO_INCREMENT,
64 session_id varchar(100) NOT NULL,
65 clicked_url text NOT NULL,
66 message_context text,
67 click_timestamp datetime DEFAULT CURRENT_TIMESTAMP,
68 user_ip varchar(45),
69 user_agent text,
70 PRIMARY KEY (id),
71 KEY session_id (session_id),
72 KEY click_timestamp (click_timestamp)
73 ) $charset_collate;";
74
75 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
76 dbDelta($sql);
77 }
78
79 /**
80 * FIXED: Robust table creation and column management
81 */
82 function mxchat_create_chat_transcripts_table() {
83 global $wpdb;
84
85 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
86 $charset_collate = $wpdb->get_charset_collate();
87
88 // Create table with ALL columns including user_name from the start
89 $sql = "CREATE TABLE $table_name (
90 id MEDIUMINT(9) NOT NULL AUTO_INCREMENT,
91 user_id MEDIUMINT(9) DEFAULT 0,
92 session_id VARCHAR(255) NOT NULL,
93 role VARCHAR(255) NOT NULL,
94 message TEXT NOT NULL,
95 user_email VARCHAR(255) DEFAULT NULL,
96 user_name VARCHAR(100) DEFAULT NULL,
97 user_identifier VARCHAR(255) DEFAULT NULL,
98 originating_page_url TEXT DEFAULT NULL,
99 originating_page_title VARCHAR(500) DEFAULT NULL,
100 timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
101 PRIMARY KEY (id),
102 KEY session_id (session_id),
103 KEY user_email (user_email),
104 KEY timestamp (timestamp)
105 ) $charset_collate;";
106
107 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
108 $result = dbDelta($sql);
109
110 // Log the result for debugging
111 if (empty($result)) {
112 //error_log("MxChat: dbDelta returned empty result for chat transcripts table");
113 } else {
114 //error_log("MxChat: dbDelta result: " . print_r($result, true));
115 }
116
117 // IMPORTANT: Ensure all columns exist for existing installations
118 mxchat_ensure_all_columns($table_name);
119 }
120
121 /**
122 * Ensure all required columns exist (for upgrades)
123 */
124 function mxchat_ensure_all_columns($table_name) {
125 global $wpdb;
126
127 // First check if table exists
128 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
129 if (!$table_exists) {
130 //error_log("MxChat: Table $table_name does not exist, cannot add columns");
131 return;
132 }
133
134 // Define all required columns and their types
135 $required_columns = [
136 'user_identifier' => 'VARCHAR(255) DEFAULT NULL',
137 'user_email' => 'VARCHAR(255) DEFAULT NULL',
138 'user_name' => 'VARCHAR(100) DEFAULT NULL',
139 'originating_page_url' => 'TEXT DEFAULT NULL',
140 'originating_page_title' => 'VARCHAR(500) DEFAULT NULL'
141 ];
142
143 // Get existing columns
144 $existing_columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name");
145 if (empty($existing_columns)) {
146 //error_log("MxChat: Could not get columns for table $table_name");
147 return;
148 }
149
150 $existing_column_names = array_column($existing_columns, 'Field');
151
152 // Add missing columns
153 foreach ($required_columns as $column_name => $column_definition) {
154 if (!in_array($column_name, $existing_column_names)) {
155 $alter_sql = "ALTER TABLE $table_name ADD COLUMN $column_name $column_definition";
156 $result = $wpdb->query($alter_sql);
157
158 if ($result === false) {
159 //error_log("MxChat: Failed to add column $column_name to $table_name. Error: " . $wpdb->last_error);
160 } else {
161 //error_log("MxChat: Successfully added column $column_name to $table_name");
162 }
163 }
164 }
165 }
166
167 /**
168 * Add role restriction column to knowledge base table
169 */
170 function mxchat_add_role_restriction_column() {
171 global $wpdb;
172 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
173
174 // Check if table exists first
175 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
176 if (!$table_exists) {
177 //error_log("MxChat: System prompt content table does not exist, cannot add role_restriction column");
178 return;
179 }
180
181 // Check if column already exists
182 $column_exists = $wpdb->get_results(
183 $wpdb->prepare(
184 "SHOW COLUMNS FROM {$table_name} LIKE %s",
185 'role_restriction'
186 )
187 );
188
189 if (empty($column_exists)) {
190 $alter_sql = "ALTER TABLE {$table_name} ADD COLUMN role_restriction VARCHAR(50) DEFAULT 'public' AFTER source_url";
191 $result = $wpdb->query($alter_sql);
192
193 if ($result === false) {
194 //error_log("MxChat: Failed to add role_restriction column. Error: " . $wpdb->last_error);
195 } else {
196 //error_log("MxChat: Successfully added role_restriction column");
197
198 // Set all existing records to 'public' (everyone can access)
199 $update_result = $wpdb->query(
200 "UPDATE {$table_name}
201 SET role_restriction = 'public'
202 WHERE role_restriction IS NULL OR role_restriction = ''"
203 );
204
205 if ($update_result !== false) {
206 //error_log("MxChat: Updated {$update_result} existing records to public access");
207 }
208 }
209 }
210 }
211
212 /**
213 * Create Pinecone role restrictions table
214 */
215 function mxchat_create_pinecone_roles_table() {
216 global $wpdb;
217
218 $table_name = $wpdb->prefix . 'mxchat_pinecone_roles';
219 $charset_collate = $wpdb->get_charset_collate();
220
221 $sql = "CREATE TABLE $table_name (
222 id mediumint(9) NOT NULL AUTO_INCREMENT,
223 vector_id varchar(255) NOT NULL,
224 source_url text,
225 role_restriction varchar(50) DEFAULT 'public',
226 updated_at timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
227 PRIMARY KEY (id),
228 UNIQUE KEY vector_id (vector_id),
229 KEY role_restriction (role_restriction)
230 ) $charset_collate;";
231
232 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
233 dbDelta($sql);
234 }
235
236 function mxchat_activate() {
237 global $wpdb;
238 $charset_collate = $wpdb->get_charset_collate();
239
240 //error_log("MxChat: Running activation function");
241
242 // Create chat transcripts table with improved function
243 mxchat_create_chat_transcripts_table();
244
245 // System Prompt Content Table
246 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
247 $sql_system_prompt = "CREATE TABLE $system_prompt_table (
248 id MEDIUMINT(9) NOT NULL AUTO_INCREMENT,
249 url VARCHAR(255) NOT NULL,
250 article_content LONGTEXT NOT NULL,
251 embedding_vector LONGTEXT,
252 source_url VARCHAR(255) DEFAULT NULL,
253 role_restriction VARCHAR(50) DEFAULT 'public',
254 timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
255 PRIMARY KEY (id)
256 ) $charset_collate;";
257
258 // Intents Table
259 $intents_table = $wpdb->prefix . 'mxchat_intents';
260 $sql_intents_table = "CREATE TABLE $intents_table (
261 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
262 intent_label VARCHAR(255) NOT NULL,
263 phrases TEXT NOT NULL,
264 embedding_vector LONGTEXT NOT NULL,
265 callback_function VARCHAR(255) NOT NULL,
266 similarity_threshold FLOAT DEFAULT 0.85,
267 enabled TINYINT(1) NOT NULL DEFAULT 1,
268 PRIMARY KEY (id)
269 ) $charset_collate;";
270
271 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
272
273 // Create other tables
274 dbDelta($sql_system_prompt);
275 dbDelta($sql_intents_table);
276
277 // Create URL click tracking table
278 mxchat_create_url_clicks_table();
279
280 //Create Pinecone roles table
281 mxchat_create_pinecone_roles_table();
282
283 // Ensure additional columns in system prompt table
284 $existing_system_columns = $wpdb->get_results("SHOW COLUMNS FROM $system_prompt_table");
285 if (!empty($existing_system_columns)) {
286 $existing_system_column_names = array_column($existing_system_columns, 'Field');
287
288 if (!in_array('embedding_vector', $existing_system_column_names)) {
289 $wpdb->query("ALTER TABLE $system_prompt_table ADD COLUMN embedding_vector LONGTEXT");
290 }
291 if (!in_array('source_url', $existing_system_column_names)) {
292 $wpdb->query("ALTER TABLE $system_prompt_table ADD COLUMN source_url VARCHAR(255) DEFAULT NULL");
293 }
294 if (!in_array('role_restriction', $existing_system_column_names)) {
295 $wpdb->query("ALTER TABLE $system_prompt_table ADD COLUMN role_restriction VARCHAR(50) DEFAULT 'public' AFTER source_url");
296 }
297 }
298
299 // Set default thresholds for existing intents
300 $wpdb->query("UPDATE {$intents_table} SET similarity_threshold = 0.85 WHERE similarity_threshold IS NULL");
301
302 // Ensure enabled column exists in intents table
303 $existing_intent_columns = $wpdb->get_results("SHOW COLUMNS FROM $intents_table");
304 if (!empty($existing_intent_columns)) {
305 $existing_intent_column_names = array_column($existing_intent_columns, 'Field');
306
307 if (!in_array('enabled', $existing_intent_column_names)) {
308 $wpdb->query("ALTER TABLE $intents_table ADD COLUMN enabled TINYINT(1) NOT NULL DEFAULT 1");
309 }
310 }
311
312 // Setup cron jobs
313 mxchat_setup_cron_jobs();
314
315 // Update version
316 update_option('mxchat_plugin_version', MXCHAT_VERSION);
317
318 //error_log("MxChat: Activation function completed");
319 }
320 /**
321 * Setup cron jobs on plugin activation
322 */
323 function mxchat_setup_cron_jobs() {
324 // Clear any existing cron jobs first
325 wp_clear_scheduled_hook('mxchat_reset_rate_limits');
326
327 // Check if WordPress cron is disabled
328 if (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) {
329 // Set flag to use fallback system
330 update_option('mxchat_use_fallback_rate_limits', true);
331 update_option('mxchat_next_rate_limit_check', time() + 3600);
332 return;
333 }
334
335 // Schedule the rate limit reset cron job
336 $result = wp_schedule_event(time() + 300, 'hourly', 'mxchat_reset_rate_limits');
337
338 if ($result === false) {
339 // Fallback if scheduling fails
340 update_option('mxchat_use_fallback_rate_limits', true);
341 update_option('mxchat_next_rate_limit_check', time() + 3600);
342 } else {
343 // Clear fallback flags if cron scheduling succeeded
344 delete_option('mxchat_use_fallback_rate_limits');
345 }
346 }
347
348 /**
349 * Clean up on plugin deactivation
350 */
351 function mxchat_deactivate() {
352 // Clear scheduled cron jobs
353 wp_clear_scheduled_hook('mxchat_reset_rate_limits');
354
355 // Clear fallback options
356 delete_option('mxchat_use_fallback_rate_limits');
357 delete_option('mxchat_next_rate_limit_check');
358 delete_option('mxchat_fallback_check_interval');
359 }
360
361 /**
362 * Check if fallback rate limit cleanup is needed
363 */
364 function mxchat_check_fallback_rate_limits() {
365 $use_fallback = get_option('mxchat_use_fallback_rate_limits', false);
366
367 if (!$use_fallback) {
368 return;
369 }
370
371 $next_check = get_option('mxchat_next_rate_limit_check', 0);
372
373 if (time() >= $next_check) {
374 // Only run reset if the MxChat_Integrator class exists
375 if (class_exists('MxChat_Integrator')) {
376 $integrator = new MxChat_Integrator();
377 if (method_exists($integrator, 'mxchat_reset_rate_limits')) {
378 $integrator->mxchat_reset_rate_limits();
379 update_option('mxchat_next_rate_limit_check', time() + 3600);
380 }
381 }
382 }
383 }
384
385 /**
386 * Robust update checking with role restriction migration
387 */
388 function mxchat_check_for_update() {
389 global $wpdb; // CRITICAL: Declare this at the top
390
391 try {
392 $current_version = get_option('mxchat_plugin_version', '0.0.0');
393 $plugin_version = MXCHAT_VERSION;
394
395 // Always run activation to ensure tables exist (safe for existing installations)
396 if ($current_version !== $plugin_version) {
397 //error_log("MxChat: Version change detected: $current_version -> $plugin_version");
398
399 // Run live agent update BEFORE updating the stored version
400 mxchat_handle_live_agent_update();
401
402 //Run role restriction migration for 2.4.0
403 if (version_compare($current_version, '2.4.0', '<')) {
404 mxchat_add_role_restriction_column();
405 }
406
407 // Run activation (this will create/update all tables and columns)
408 mxchat_activate();
409
410 // Run migration functions
411 mxchat_migrate_live_agent_status();
412
413 // Add the cleanup function for version 2.1.8
414 if (version_compare($current_version, '2.1.8', '<')) {
415 $deleted = mxchat_cleanup_orphaned_chat_history();
416 }
417
418 // Update version LAST
419 update_option('mxchat_plugin_version', $plugin_version);
420
421 //error_log("MxChat: Updated from version $current_version to $plugin_version");
422 }
423
424 // CRITICAL: Always ensure tables exist, even if version matches
425 // This handles cases where tables were manually deleted
426 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
427 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
428
429 if (!$table_exists) {
430 //error_log("MxChat: Chat transcripts table missing, recreating...");
431 mxchat_activate(); // Run full activation instead of just table creation
432 }
433
434 } catch (Exception $e) {
435 //error_log('MxChat update error: ' . $e->getMessage());
436 // Don't update version if there was an error
437 }
438 }
439
440 /**
441 * Ensure tables exist on every load for fresh installations
442 */
443 function mxchat_ensure_tables_exist() {
444 global $wpdb;
445
446 // Only run for admin users to avoid performance impact
447 if (!current_user_can('administrator')) {
448 return;
449 }
450
451 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
452 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
453
454 if (!$table_exists) {
455 //error_log("MxChat: Tables missing on admin load, running activation");
456 mxchat_activate();
457 }
458 }
459
460 /**
461 * Clean up orphaned chat history options from the wp_options table
462 * @return int Number of options deleted
463 */
464 function mxchat_cleanup_orphaned_chat_history() {
465 global $wpdb;
466 $count = 0;
467
468 // Get all option keys that match our pattern
469 $history_options = $wpdb->get_results(
470 "SELECT option_name FROM {$wpdb->options}
471 WHERE option_name LIKE 'mxchat_history_%'"
472 );
473
474 if (!empty($history_options)) {
475 foreach ($history_options as $option) {
476 // Extract the session ID from the option name
477 $session_id = str_replace('mxchat_history_', '', $option->option_name);
478
479 // Check if this session still exists in the custom table
480 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
481 $exists = $wpdb->get_var(
482 $wpdb->prepare(
483 "SELECT COUNT(*) FROM {$table_name} WHERE session_id = %s",
484 $session_id
485 )
486 );
487
488 // If session doesn't exist in the main table, delete the option
489 if ($exists == 0) {
490 delete_option($option->option_name);
491 // Also delete related metadata
492 delete_option("mxchat_email_{$session_id}");
493 delete_option("mxchat_name_{$session_id}");
494 delete_option("mxchat_agent_name_{$session_id}");
495 $count++;
496 }
497 }
498 }
499
500 return $count;
501 }
502
503 function mxchat_migrate_live_agent_status() {
504 $options = get_option('mxchat_options', []);
505
506 // Check if live_agent_status exists
507 if (isset($options['live_agent_status'])) {
508 $current_status = $options['live_agent_status'];
509 $needs_update = false;
510
511 // Convert to new format if needed
512 if ($current_status === 'online') {
513 $options['live_agent_status'] = 'on';
514 $needs_update = true;
515 } else if ($current_status === 'offline') {
516 $options['live_agent_status'] = 'off';
517 $needs_update = true;
518 } else if (!in_array($current_status, ['on', 'off'])) {
519 // Default to off for any unexpected values
520 $options['live_agent_status'] = 'off';
521 $needs_update = true;
522 }
523
524 // Only update if needed
525 if ($needs_update) {
526 update_option('mxchat_options', $options);
527 }
528 } else {
529 // If status doesn't exist, set default to off
530 $options['live_agent_status'] = 'off';
531 update_option('mxchat_options', $options);
532 }
533 }
534
535 function mxchat_handle_live_agent_update() {
536 // Get the CURRENT stored version (before it gets updated)
537 $current_version = get_option('mxchat_plugin_version', '0.0.0');
538 $new_version = '2.2.2';
539
540 // Only run this once for the update to 2.2.2
541 $update_handled = get_option('mxchat_live_agent_update_2_2_2_handled', false);
542
543 // Check if we're upgrading TO 2.2.2 and haven't handled this yet
544 if (version_compare($current_version, $new_version, '<') && !$update_handled) {
545 $options = get_option('mxchat_options', array());
546
547 // Check if live agent was previously enabled
548 if (isset($options['live_agent_status']) && $options['live_agent_status'] === 'on') {
549 // Disable live agent
550 $options['live_agent_status'] = 'off';
551 update_option('mxchat_options', $options);
552
553 // Set flag to show the notification banner
554 update_option('mxchat_show_live_agent_disabled_notice', true);
555 }
556
557 // Mark this update as handled
558 update_option('mxchat_live_agent_update_2_2_2_handled', true);
559 }
560 }
561
562 // Initialize plugin safely
563 function mxchat_init() {
564 // Include all class files first
565 mxchat_include_classes();
566
567 // Run update check
568 mxchat_check_for_update();
569
570 // CRITICAL: Ensure tables exist (for fresh installations that don't trigger activation hook)
571 add_action('admin_init', 'mxchat_ensure_tables_exist', 1);
572
573 // Add fallback rate limit check
574 add_action('init', 'mxchat_check_fallback_rate_limits', 5);
575
576 // Initialize classes with error handling
577 try {
578 // Initialize admin classes
579 if (is_admin()) {
580 if (class_exists('MxChat_Knowledge_Manager')) {
581 $mxchat_knowledge_manager = new MxChat_Knowledge_Manager();
582
583 if (class_exists('MxChat_Admin')) {
584 $mxchat_admin = new MxChat_Admin($mxchat_knowledge_manager);
585 }
586 }
587
588 // Initialize meta box class
589 if (class_exists('MxChat_Meta_Box')) {
590 new MxChat_Meta_Box();
591 }
592 }
593
594 // Initialize public classes
595 if (class_exists('MxChat_Public')) {
596 $mxchat_public = new MxChat_Public();
597 }
598
599 if (class_exists('MxChat_Integrator')) {
600 $mxchat_integrator = new MxChat_Integrator();
601 }
602
603 } catch (Exception $e) {
604 //error_log('MxChat initialization error: ' . $e->getMessage());
605
606 // Show admin notice if there's an error
607 if (is_admin()) {
608 add_action('admin_notices', function() use ($e) {
609 echo '<div class="notice notice-error"><p>';
610 echo '<strong>MxChat Error:</strong> Plugin initialization failed. ';
611 echo 'Please check error logs or contact support. Error: ' . esc_html($e->getMessage());
612 echo '</p></div>';
613 });
614 }
615 }
616 }
617
618 // Run initialization on plugins_loaded
619 add_action('plugins_loaded', 'mxchat_init');
620
621 // Register activation hook
622 register_activation_hook(__FILE__, 'mxchat_activate');
623
624 // Add cron schedule
625 add_filter('cron_schedules', function($schedules) {
626 $schedules['one_minute'] = array(
627 'interval' => 60,
628 'display' => 'Every Minute'
629 );
630 return $schedules;
631 });
632
633 // Register deactivation hook
634 register_deactivation_hook(__FILE__, 'mxchat_deactivate');