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

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

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