PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.5.7
MxChat – AI Chatbot & Content Generation for WordPress v2.5.7
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / mxchat-basic.php

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

970 lines 34.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 * Plugin URI: https://mxchat.ai/
5 * Description: AI chatbot for WordPress with OpenAI, Claude, xAI, DeepSeek, live agent, PDF uploads, WooCommerce, and training on website data.
6 * Version: 2.5.7
7 * Author: MxChat
8 * Author URI: https://mxchat.ai
9 * License: GPLv2 or later
10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
11 * Text Domain: mxchat
12 * Domain Path: /languages
13 */
14
15 if (!defined('ABSPATH')) {
16 exit; // Exit if accessed directly.
17 }
18
19 // Define plugin version constant for asset versioning
20 // Reads version from plugin header automatically
21 if (!defined('MXCHAT_VERSION')) {
22 $plugin_data = get_file_data(__FILE__, array('Version' => 'Version'), 'plugin');
23 define('MXCHAT_VERSION', $plugin_data['Version']);
24 }
25
26 function mxchat_load_textdomain() {
27 load_plugin_textdomain('mxchat', false, dirname(plugin_basename(__FILE__)) . '/languages');
28 }
29 add_action('init', 'mxchat_load_textdomain');
30
31 // Include classes with error handling
32 function mxchat_include_classes() {
33 $class_files = array(
34 'includes/class-mxchat-integrator.php',
35 'includes/class-mxchat-admin.php',
36 'includes/class-mxchat-public.php',
37 'includes/class-mxchat-utils.php',
38 'includes/class-mxchat-user.php',
39 'includes/class-mxchat-meta-box.php',
40 'includes/pdf-parser/alt_autoload.php',
41 'includes/class-mxchat-word-handler.php',
42 'admin/class-ajax-handler.php',
43 'admin/class-pinecone-manager.php',
44 'admin/class-knowledge-manager.php'
45 );
46
47 foreach ($class_files as $file) {
48 $file_path = plugin_dir_path(__FILE__) . $file;
49 if (file_exists($file_path)) {
50 require_once $file_path;
51 } else {
52 //error_log('MxChat: Missing class file - ' . $file);
53 }
54 }
55 }
56
57 /**
58 * Create URL click tracking table
59 */
60 function mxchat_create_url_clicks_table() {
61 global $wpdb;
62
63 $table_name = $wpdb->prefix . 'mxchat_url_clicks';
64
65 $charset_collate = $wpdb->get_charset_collate();
66
67 $sql = "CREATE TABLE $table_name (
68 id mediumint(9) NOT NULL AUTO_INCREMENT,
69 session_id varchar(100) NOT NULL,
70 clicked_url text NOT NULL,
71 message_context text,
72 click_timestamp datetime DEFAULT CURRENT_TIMESTAMP,
73 user_ip varchar(45),
74 user_agent text,
75 PRIMARY KEY (id),
76 KEY session_id (session_id),
77 KEY click_timestamp (click_timestamp)
78 ) $charset_collate;";
79
80 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
81 dbDelta($sql);
82 }
83
84 /**
85 * FIXED: Robust table creation and column management
86 */
87 function mxchat_create_chat_transcripts_table() {
88 global $wpdb;
89
90 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
91 $charset_collate = $wpdb->get_charset_collate();
92
93 // Create table with ALL columns including user_name from the start
94 $sql = "CREATE TABLE $table_name (
95 id MEDIUMINT(9) NOT NULL AUTO_INCREMENT,
96 user_id MEDIUMINT(9) DEFAULT 0,
97 session_id VARCHAR(255) NOT NULL,
98 role VARCHAR(255) NOT NULL,
99 message TEXT NOT NULL,
100 user_email VARCHAR(255) DEFAULT NULL,
101 user_name VARCHAR(100) DEFAULT NULL,
102 user_identifier VARCHAR(255) DEFAULT NULL,
103 originating_page_url TEXT DEFAULT NULL,
104 originating_page_title VARCHAR(500) DEFAULT NULL,
105 timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
106 PRIMARY KEY (id),
107 KEY session_id (session_id),
108 KEY user_email (user_email),
109 KEY timestamp (timestamp)
110 ) $charset_collate;";
111
112 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
113 $result = dbDelta($sql);
114
115 // Log the result for debugging
116 if (empty($result)) {
117 //error_log("MxChat: dbDelta returned empty result for chat transcripts table");
118 } else {
119 //error_log("MxChat: dbDelta result: " . print_r($result, true));
120 }
121
122 // IMPORTANT: Ensure all columns exist for existing installations
123 mxchat_ensure_all_columns($table_name);
124 }
125
126 /**
127 * Ensure all required columns exist (for upgrades)
128 */
129 function mxchat_ensure_all_columns($table_name) {
130 global $wpdb;
131
132 // First check if table exists
133 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
134 if (!$table_exists) {
135 //error_log("MxChat: Table $table_name does not exist, cannot add columns");
136 return;
137 }
138
139 // Define all required columns and their types
140 $required_columns = [
141 'user_identifier' => 'VARCHAR(255) DEFAULT NULL',
142 'user_email' => 'VARCHAR(255) DEFAULT NULL',
143 'user_name' => 'VARCHAR(100) DEFAULT NULL',
144 'originating_page_url' => 'TEXT DEFAULT NULL',
145 'originating_page_title' => 'VARCHAR(500) DEFAULT NULL'
146 ];
147
148 // Get existing columns
149 $existing_columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name");
150 if (empty($existing_columns)) {
151 //error_log("MxChat: Could not get columns for table $table_name");
152 return;
153 }
154
155 $existing_column_names = array_column($existing_columns, 'Field');
156
157 // Add missing columns
158 foreach ($required_columns as $column_name => $column_definition) {
159 if (!in_array($column_name, $existing_column_names)) {
160 $alter_sql = "ALTER TABLE $table_name ADD COLUMN $column_name $column_definition";
161 $result = $wpdb->query($alter_sql);
162
163 if ($result === false) {
164 //error_log("MxChat: Failed to add column $column_name to $table_name. Error: " . $wpdb->last_error);
165 } else {
166 //error_log("MxChat: Successfully added column $column_name to $table_name");
167 }
168 }
169 }
170 }
171
172 /**
173 * Add role restriction column to knowledge base table
174 */
175 function mxchat_add_role_restriction_column() {
176 global $wpdb;
177 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
178
179 // Check if table exists first
180 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
181 if (!$table_exists) {
182 //error_log("MxChat: System prompt content table does not exist, cannot add role_restriction column");
183 return;
184 }
185
186 // Check if column already exists
187 $column_exists = $wpdb->get_results(
188 $wpdb->prepare(
189 "SHOW COLUMNS FROM {$table_name} LIKE %s",
190 'role_restriction'
191 )
192 );
193
194 if (empty($column_exists)) {
195 $alter_sql = "ALTER TABLE {$table_name} ADD COLUMN role_restriction VARCHAR(50) DEFAULT 'public' AFTER source_url";
196 $result = $wpdb->query($alter_sql);
197
198 if ($result === false) {
199 //error_log("MxChat: Failed to add role_restriction column. Error: " . $wpdb->last_error);
200 } else {
201 //error_log("MxChat: Successfully added role_restriction column");
202
203 // Set all existing records to 'public' (everyone can access)
204 $update_result = $wpdb->query(
205 "UPDATE {$table_name}
206 SET role_restriction = 'public'
207 WHERE role_restriction IS NULL OR role_restriction = ''"
208 );
209
210 if ($update_result !== false) {
211 //error_log("MxChat: Updated {$update_result} existing records to public access");
212 }
213 }
214 }
215 }
216
217 /**
218 * Add enabled_bots column to intents table for multi-bot action filtering
219 */
220 function mxchat_add_enabled_bots_column() {
221 global $wpdb;
222 $table_name = $wpdb->prefix . 'mxchat_intents';
223
224 // Check if table exists first
225 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
226 if (!$table_exists) {
227 //error_log("MxChat: Intents table does not exist, cannot add enabled_bots column");
228 return;
229 }
230
231 // Check if column already exists
232 $column_exists = $wpdb->get_results(
233 $wpdb->prepare(
234 "SHOW COLUMNS FROM {$table_name} LIKE %s",
235 'enabled_bots'
236 )
237 );
238
239 if (empty($column_exists)) {
240 $alter_sql = "ALTER TABLE {$table_name} ADD COLUMN enabled_bots LONGTEXT DEFAULT NULL AFTER enabled";
241 $result = $wpdb->query($alter_sql);
242
243 if ($result === false) {
244 //error_log("MxChat: Failed to add enabled_bots column. Error: " . $wpdb->last_error);
245 } else {
246 //error_log("MxChat: Successfully added enabled_bots column");
247
248 // Set all existing actions to work with 'default' bot for backward compatibility
249 $default_bots = json_encode(['default']);
250 $update_result = $wpdb->query(
251 $wpdb->prepare(
252 "UPDATE {$table_name}
253 SET enabled_bots = %s
254 WHERE enabled_bots IS NULL OR enabled_bots = ''",
255 $default_bots
256 )
257 );
258
259 if ($update_result !== false) {
260 //error_log("MxChat: Updated {$update_result} existing actions to work with default bot");
261 }
262 }
263 }
264 }
265
266 /**
267 * Create Pinecone role restrictions table with multi-bot support
268 */
269 function mxchat_create_pinecone_roles_table() {
270 global $wpdb;
271
272 $table_name = $wpdb->prefix . 'mxchat_pinecone_roles';
273 $charset_collate = $wpdb->get_charset_collate();
274
275 $sql = "CREATE TABLE $table_name (
276 id mediumint(9) NOT NULL AUTO_INCREMENT,
277 vector_id varchar(255) NOT NULL,
278 bot_id varchar(50) NOT NULL DEFAULT 'default',
279 source_url text,
280 role_restriction varchar(50) DEFAULT 'public',
281 updated_at timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
282 PRIMARY KEY (id),
283 UNIQUE KEY vector_bot (vector_id, bot_id),
284 KEY role_restriction (role_restriction),
285 KEY bot_id (bot_id)
286 ) $charset_collate;";
287
288 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
289 dbDelta($sql);
290 }
291
292 /**
293 * Add bot_id column to mxchat_pinecone_roles table for multi-bot support
294 * This migration runs once to update existing installations
295 */
296 function mxchat_migrate_pinecone_roles_add_bot_id() {
297 global $wpdb;
298
299 // Check if migration already ran
300 $migration_version = get_option('mxchat_pinecone_roles_migration_version', '0');
301 if (version_compare($migration_version, '2.5.2', '>=')) {
302 return; // Already migrated
303 }
304
305 $table_name = $wpdb->prefix . 'mxchat_pinecone_roles';
306
307 // Check if table exists
308 if ($wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") != $table_name) {
309 return; // Table doesn't exist yet
310 }
311
312 // Check if bot_id column already exists
313 $column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$table_name} LIKE 'bot_id'");
314
315 if (empty($column_exists)) {
316 // Add bot_id column
317 $wpdb->query("ALTER TABLE {$table_name} ADD COLUMN bot_id VARCHAR(50) NOT NULL DEFAULT 'default' AFTER vector_id");
318
319 // Update the unique key to include bot_id
320 $wpdb->query("ALTER TABLE {$table_name} DROP INDEX vector_id");
321 $wpdb->query("ALTER TABLE {$table_name} ADD UNIQUE KEY vector_bot (vector_id, bot_id)");
322
323 // Add index for bot_id
324 $wpdb->query("ALTER TABLE {$table_name} ADD KEY bot_id (bot_id)");
325
326 error_log('MxChat: Successfully added bot_id column to mxchat_pinecone_roles table');
327 }
328
329 // Mark migration as complete
330 update_option('mxchat_pinecone_roles_migration_version', '2.5.2');
331 }
332
333 /**
334 * 2.5.6: Add content_type column to mxchat_system_prompt_content table
335 * Enables filtering knowledge base by content type (posts, pages, PDFs, etc.)
336 */
337 function mxchat_migrate_add_content_type_column() {
338 global $wpdb;
339
340 // Check if migration already ran
341 $migration_version = get_option('mxchat_content_type_migration_version', '0');
342 if (version_compare($migration_version, '2.5.6', '>=')) {
343 return;
344 }
345
346 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
347
348 // Check if table exists
349 if ($wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") != $table_name) {
350 return;
351 }
352
353 // Check if content_type column already exists
354 $column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$table_name} LIKE 'content_type'");
355
356 if (empty($column_exists)) {
357 // Add content_type column with default value 'content' for backwards compatibility
358 $wpdb->query("ALTER TABLE {$table_name} ADD COLUMN content_type VARCHAR(50) DEFAULT 'content' AFTER role_restriction");
359
360 // Add index for better query performance
361 $wpdb->query("ALTER TABLE {$table_name} ADD KEY content_type (content_type)");
362
363 error_log('MxChat: Successfully added content_type column to mxchat_system_prompt_content table');
364 }
365
366 // Mark migration as complete
367 update_option('mxchat_content_type_migration_version', '2.5.6');
368 }
369
370 /**
371 * 2.5.2: Create queue processing tables for reliable background processing
372 */
373 function mxchat_create_queue_tables() {
374 global $wpdb;
375 $charset_collate = $wpdb->get_charset_collate();
376
377 // Main queue table
378 $queue_table = $wpdb->prefix . 'mxchat_processing_queue';
379 $sql_queue = "CREATE TABLE $queue_table (
380 id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
381 queue_id varchar(64) NOT NULL,
382 item_type varchar(20) NOT NULL,
383 item_data longtext NOT NULL,
384 status varchar(20) NOT NULL DEFAULT 'pending',
385 bot_id varchar(50) NOT NULL DEFAULT 'default',
386 priority int(11) NOT NULL DEFAULT 0,
387 attempts int(11) NOT NULL DEFAULT 0,
388 max_attempts int(11) NOT NULL DEFAULT 3,
389 error_message text DEFAULT NULL,
390 created_at datetime NOT NULL,
391 started_at datetime DEFAULT NULL,
392 completed_at datetime DEFAULT NULL,
393 PRIMARY KEY (id),
394 KEY queue_id (queue_id),
395 KEY status (status),
396 KEY item_type (item_type),
397 KEY priority (priority)
398 ) $charset_collate;";
399
400 // Queue metadata table
401 $meta_table = $wpdb->prefix . 'mxchat_queue_meta';
402 $sql_meta = "CREATE TABLE $meta_table (
403 id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
404 queue_id varchar(64) NOT NULL,
405 meta_key varchar(255) NOT NULL,
406 meta_value longtext,
407 PRIMARY KEY (id),
408 KEY queue_id (queue_id),
409 KEY meta_key (meta_key)
410 ) $charset_collate;";
411
412 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
413 dbDelta($sql_queue);
414 dbDelta($sql_meta);
415
416 //error_log("MxChat: Queue tables created/updated successfully");
417 }
418
419 /**
420 * 2.5.2: Fix URL column size to support long URLs (especially with UTF-8 encoding)
421 * This fixes "url, source_url. The supplied values may be too long" errors
422 */
423 function mxchat_fix_url_column_size() {
424 global $wpdb;
425 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
426
427 // Check if table exists
428 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
429 if (!$table_exists) {
430 return;
431 }
432
433 // Change url and source_url from VARCHAR to TEXT to handle long URLs
434 // This is especially important for URLs with UTF-8 encoded characters (Hebrew, Arabic, etc.)
435 $wpdb->query("ALTER TABLE {$table_name} MODIFY COLUMN url TEXT");
436 $wpdb->query("ALTER TABLE {$table_name} MODIFY COLUMN source_url TEXT");
437
438 //error_log("MxChat: Successfully updated url and source_url columns to TEXT type for long URL support");
439 }
440
441 /**
442 * Migrate deprecated AI models to their replacements
443 * Version 2.5.1: Migrate Claude 3.5 Sonnet (deprecated) to Claude 3.7 Sonnet
444 */
445 function mxchat_migrate_deprecated_models() {
446 $options = get_option('mxchat_options', array());
447
448 // Check if model is set and is the deprecated Claude 3.5 Sonnet
449 if (isset($options['model']) && $options['model'] === 'claude-3-5-sonnet-20241022') {
450 // Update to Claude 3.7 Sonnet (the replacement model)
451 $options['model'] = 'claude-3-7-sonnet-20250219';
452 update_option('mxchat_options', $options);
453
454 // Set a flag to show admin notice
455 update_option('mxchat_model_migrated_notice', true);
456
457 //error_log('MxChat: Migrated deprecated Claude 3.5 Sonnet to Claude 3.7 Sonnet');
458 }
459 }
460
461 /**
462 * Show admin notice after model migration
463 */
464 function mxchat_show_migration_notice() {
465 if (get_option('mxchat_model_migrated_notice')) {
466 ?>
467 <div class="notice notice-info is-dismissible">
468 <p>
469 <strong><?php esc_html_e('MxChat Model Updated', 'mxchat'); ?></strong><br>
470 <?php esc_html_e('Your chatbot model has been automatically updated from Claude 3.5 Sonnet to Claude 3.7 Sonnet due to the deprecation of the previous model by Anthropic. Claude 3.7 Sonnet offers improved performance and capabilities.', 'mxchat'); ?>
471 </p>
472 </div>
473 <?php
474 delete_option('mxchat_model_migrated_notice');
475 }
476 }
477
478 function mxchat_activate() {
479 global $wpdb;
480 $charset_collate = $wpdb->get_charset_collate();
481
482 //error_log("MxChat: Running activation function");
483
484 // Create chat transcripts table with improved function
485 mxchat_create_chat_transcripts_table();
486
487 // System Prompt Content Table - UPDATED: Use TEXT for url and source_url columns
488 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
489 $sql_system_prompt = "CREATE TABLE $system_prompt_table (
490 id MEDIUMINT(9) NOT NULL AUTO_INCREMENT,
491 url TEXT NOT NULL,
492 article_content LONGTEXT NOT NULL,
493 embedding_vector LONGTEXT,
494 source_url TEXT DEFAULT NULL,
495 role_restriction VARCHAR(50) DEFAULT 'public',
496 content_type VARCHAR(50) DEFAULT 'content',
497 timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
498 PRIMARY KEY (id),
499 KEY content_type (content_type)
500 ) $charset_collate;";
501
502 // Intents Table - NOW INCLUDES enabled_bots column from the start
503 $intents_table = $wpdb->prefix . 'mxchat_intents';
504 $sql_intents_table = "CREATE TABLE $intents_table (
505 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
506 intent_label VARCHAR(255) NOT NULL,
507 phrases TEXT NOT NULL,
508 embedding_vector LONGTEXT NOT NULL,
509 callback_function VARCHAR(255) NOT NULL,
510 similarity_threshold FLOAT DEFAULT 0.85,
511 enabled TINYINT(1) NOT NULL DEFAULT 1,
512 enabled_bots LONGTEXT DEFAULT NULL,
513 PRIMARY KEY (id)
514 ) $charset_collate;";
515
516 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
517
518 // Create other tables
519 dbDelta($sql_system_prompt);
520 dbDelta($sql_intents_table);
521
522 // Create URL click tracking table
523 mxchat_create_url_clicks_table();
524
525 // Create Pinecone roles table
526 mxchat_create_pinecone_roles_table();
527
528 // NEW 2.5.2: Create queue processing tables
529 mxchat_create_queue_tables();
530
531 // Ensure additional columns in system prompt table
532 $existing_system_columns = $wpdb->get_results("SHOW COLUMNS FROM $system_prompt_table");
533 if (!empty($existing_system_columns)) {
534 $existing_system_column_names = array_column($existing_system_columns, 'Field');
535
536 if (!in_array('embedding_vector', $existing_system_column_names)) {
537 $wpdb->query("ALTER TABLE $system_prompt_table ADD COLUMN embedding_vector LONGTEXT");
538 }
539 if (!in_array('source_url', $existing_system_column_names)) {
540 $wpdb->query("ALTER TABLE $system_prompt_table ADD COLUMN source_url TEXT DEFAULT NULL");
541 }
542 if (!in_array('role_restriction', $existing_system_column_names)) {
543 $wpdb->query("ALTER TABLE $system_prompt_table ADD COLUMN role_restriction VARCHAR(50) DEFAULT 'public' AFTER source_url");
544 }
545 }
546
547 // Set default thresholds for existing intents
548 $wpdb->query("UPDATE {$intents_table} SET similarity_threshold = 0.85 WHERE similarity_threshold IS NULL");
549
550 // Ensure enabled column exists in intents table
551 $existing_intent_columns = $wpdb->get_results("SHOW COLUMNS FROM $intents_table");
552 if (!empty($existing_intent_columns)) {
553 $existing_intent_column_names = array_column($existing_intent_columns, 'Field');
554
555 if (!in_array('enabled', $existing_intent_column_names)) {
556 $wpdb->query("ALTER TABLE $intents_table ADD COLUMN enabled TINYINT(1) NOT NULL DEFAULT 1");
557 }
558
559 // Ensure enabled_bots column exists for existing installations
560 if (!in_array('enabled_bots', $existing_intent_column_names)) {
561 $wpdb->query("ALTER TABLE $intents_table ADD COLUMN enabled_bots LONGTEXT DEFAULT NULL AFTER enabled");
562
563 // Set existing actions to work with default bot
564 $default_bots = json_encode(['default']);
565 $wpdb->query($wpdb->prepare(
566 "UPDATE {$intents_table} SET enabled_bots = %s WHERE enabled_bots IS NULL",
567 $default_bots
568 ));
569 }
570 }
571
572 // Run migration for existing installations
573 mxchat_migrate_pinecone_roles_add_bot_id();
574
575 // Setup cron jobs
576 mxchat_setup_cron_jobs();
577
578 // Update version
579 update_option('mxchat_plugin_version', MXCHAT_VERSION);
580
581 //error_log("MxChat: Activation function completed");
582 }
583
584 /**
585 * Setup cron jobs on plugin activation
586 */
587 function mxchat_setup_cron_jobs() {
588 // Clear any existing cron jobs first
589 wp_clear_scheduled_hook('mxchat_reset_rate_limits');
590
591 // Check if WordPress cron is disabled
592 if (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) {
593 // Set flag to use fallback system
594 update_option('mxchat_use_fallback_rate_limits', true);
595 update_option('mxchat_next_rate_limit_check', time() + 3600);
596 return;
597 }
598
599 // Schedule the rate limit reset cron job
600 $result = wp_schedule_event(time() + 300, 'hourly', 'mxchat_reset_rate_limits');
601
602 if ($result === false) {
603 // Fallback if scheduling fails
604 update_option('mxchat_use_fallback_rate_limits', true);
605 update_option('mxchat_next_rate_limit_check', time() + 3600);
606 } else {
607 // Clear fallback flags if cron scheduling succeeded
608 delete_option('mxchat_use_fallback_rate_limits');
609 }
610
611 // Schedule transcript cleanup if configured
612 $transcript_options = get_option('mxchat_transcripts_options', array());
613 $cleanup_interval = isset($transcript_options['mxchat_auto_delete_transcripts']) ? $transcript_options['mxchat_auto_delete_transcripts'] : 'never';
614
615 if ($cleanup_interval !== 'never') {
616 // Check if not already scheduled
617 if (!wp_next_scheduled('mxchat_cleanup_old_transcripts')) {
618 // Schedule to run daily at 3 AM
619 $next_run = strtotime('tomorrow 3:00 AM');
620 wp_schedule_event($next_run, 'daily', 'mxchat_cleanup_old_transcripts');
621 }
622 }
623 }
624
625 /**
626 * Clean up on plugin deactivation
627 */
628 function mxchat_deactivate() {
629 // Clear scheduled cron jobs
630 wp_clear_scheduled_hook('mxchat_reset_rate_limits');
631 wp_clear_scheduled_hook('mxchat_cleanup_old_transcripts');
632
633 // Clear fallback options
634 delete_option('mxchat_use_fallback_rate_limits');
635 delete_option('mxchat_next_rate_limit_check');
636 delete_option('mxchat_fallback_check_interval');
637
638 // NOTE: We do NOT delete queue tables on deactivation
639 // This preserves data if user accidentally deactivates the plugin
640 }
641
642 /**
643 * Check if fallback rate limit cleanup is needed
644 */
645 function mxchat_check_fallback_rate_limits() {
646 $use_fallback = get_option('mxchat_use_fallback_rate_limits', false);
647
648 if (!$use_fallback) {
649 return;
650 }
651
652 $next_check = get_option('mxchat_next_rate_limit_check', 0);
653
654 if (time() >= $next_check) {
655 // Only run reset if the MxChat_Integrator class exists
656 if (class_exists('MxChat_Integrator')) {
657 $integrator = new MxChat_Integrator();
658 if (method_exists($integrator, 'mxchat_reset_rate_limits')) {
659 $integrator->mxchat_reset_rate_limits();
660 update_option('mxchat_next_rate_limit_check', time() + 3600);
661 }
662 }
663 }
664 }
665
666 /**
667 * Robust update checking with role restriction migration, model deprecation, and queue tables
668 * CRITICAL: This runs on EVERY page load to ensure tables exist
669 */
670 function mxchat_check_for_update() {
671 global $wpdb;
672
673 try {
674 $current_version = get_option('mxchat_plugin_version', '0.0.0');
675 $plugin_version = MXCHAT_VERSION;
676
677 // Always ensure critical tables exist (even if version matches)
678 // This handles manual table deletion or fresh installs
679 $chat_table = $wpdb->prefix . 'mxchat_chat_transcripts';
680 $queue_table = $wpdb->prefix . 'mxchat_processing_queue';
681
682 $chat_exists = $wpdb->get_var("SHOW TABLES LIKE '$chat_table'") === $chat_table;
683 $queue_exists = $wpdb->get_var("SHOW TABLES LIKE '$queue_table'") === $queue_table;
684
685 if (!$chat_exists || !$queue_exists) {
686 //error_log("MxChat: Critical tables missing, running activation");
687 mxchat_activate();
688 }
689
690 // Version-specific migrations
691 if ($current_version !== $plugin_version) {
692 //error_log("MxChat: Version change detected: $current_version -> $plugin_version");
693
694 // Run live agent update BEFORE updating the stored version
695 mxchat_handle_live_agent_update();
696
697 // Run role restriction migration for 2.4.1
698 if (version_compare($current_version, '2.4.1', '<')) {
699 mxchat_add_role_restriction_column();
700 }
701
702 // Run enabled_bots column migration for 2.4.4
703 if (version_compare($current_version, '2.4.4', '<')) {
704 mxchat_add_enabled_bots_column();
705 }
706
707 // Run model migration for 2.5.1 (Claude deprecation)
708 if (version_compare($current_version, '2.5.1', '<')) {
709 mxchat_migrate_deprecated_models();
710 }
711
712 // 2.5.2: Ensure queue tables exist and fix URL column sizes for all users upgrading to 2.5.2
713 if (version_compare($current_version, '2.5.2', '<')) {
714 mxchat_create_queue_tables();
715 mxchat_fix_url_column_size(); // NEW: Fix URL column size for long URLs
716 //error_log("MxChat: Queue tables created and URL columns updated for upgrade to 2.5.2");
717 }
718
719 // Run full activation to ensure everything is up to date
720 mxchat_activate();
721
722 // Run migration functions
723 mxchat_migrate_live_agent_status();
724
725 // Add the cleanup function for version 2.1.8
726 if (version_compare($current_version, '2.1.8', '<')) {
727 $deleted = mxchat_cleanup_orphaned_chat_history();
728 }
729
730 // Update version LAST
731 update_option('mxchat_plugin_version', $plugin_version);
732
733 //error_log("MxChat: Updated from version $current_version to $plugin_version");
734 }
735
736 } catch (Exception $e) {
737 //error_log('MxChat update error: ' . $e->getMessage());
738 // Don't update version if there was an error
739 }
740 }
741
742 /**
743 * Ensure tables exist on every admin load for fresh installations
744 * This is a safety net for cases where activation hook doesn't fire
745 */
746 function mxchat_ensure_tables_exist() {
747 global $wpdb;
748
749 // Only run for admin users to avoid performance impact
750 if (!current_user_can('administrator')) {
751 return;
752 }
753
754 // Check if we've already verified tables in this session
755 static $tables_checked = false;
756 if ($tables_checked) {
757 return;
758 }
759 $tables_checked = true;
760
761 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
762 $queue_table = $wpdb->prefix . 'mxchat_processing_queue';
763
764 $chat_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
765 $queue_exists = $wpdb->get_var("SHOW TABLES LIKE '$queue_table'") === $queue_table;
766
767 if (!$chat_exists || !$queue_exists) {
768 //error_log("MxChat: Tables missing on admin load, running activation");
769 mxchat_activate();
770 }
771 }
772
773 /**
774 * Clean up orphaned chat history options from the wp_options table
775 * @return int Number of options deleted
776 */
777 function mxchat_cleanup_orphaned_chat_history() {
778 global $wpdb;
779 $count = 0;
780
781 // Get all option keys that match our pattern
782 $history_options = $wpdb->get_results(
783 "SELECT option_name FROM {$wpdb->options}
784 WHERE option_name LIKE 'mxchat_history_%'"
785 );
786
787 if (!empty($history_options)) {
788 foreach ($history_options as $option) {
789 // Extract the session ID from the option name
790 $session_id = str_replace('mxchat_history_', '', $option->option_name);
791
792 // Check if this session still exists in the custom table
793 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
794 $exists = $wpdb->get_var(
795 $wpdb->prepare(
796 "SELECT COUNT(*) FROM {$table_name} WHERE session_id = %s",
797 $session_id
798 )
799 );
800
801 // If session doesn't exist in the main table, delete the option
802 if ($exists == 0) {
803 delete_option($option->option_name);
804 // Also delete related metadata
805 delete_option("mxchat_email_{$session_id}");
806 delete_option("mxchat_name_{$session_id}");
807 delete_option("mxchat_agent_name_{$session_id}");
808 $count++;
809 }
810 }
811 }
812
813 return $count;
814 }
815
816 function mxchat_migrate_live_agent_status() {
817 $options = get_option('mxchat_options', []);
818
819 // Check if live_agent_status exists
820 if (isset($options['live_agent_status'])) {
821 $current_status = $options['live_agent_status'];
822 $needs_update = false;
823
824 // Convert to new format if needed
825 if ($current_status === 'online') {
826 $options['live_agent_status'] = 'on';
827 $needs_update = true;
828 } else if ($current_status === 'offline') {
829 $options['live_agent_status'] = 'off';
830 $needs_update = true;
831 } else if (!in_array($current_status, ['on', 'off'])) {
832 // Default to off for any unexpected values
833 $options['live_agent_status'] = 'off';
834 $needs_update = true;
835 }
836
837 // Only update if needed
838 if ($needs_update) {
839 update_option('mxchat_options', $options);
840 }
841 } else {
842 // If status doesn't exist, set default to off
843 $options['live_agent_status'] = 'off';
844 update_option('mxchat_options', $options);
845 }
846 }
847
848 function mxchat_handle_live_agent_update() {
849 // Get the CURRENT stored version (before it gets updated)
850 $current_version = get_option('mxchat_plugin_version', '0.0.0');
851 $new_version = '2.2.2';
852
853 // Only run this once for the update to 2.2.2
854 $update_handled = get_option('mxchat_live_agent_update_2_2_2_handled', false);
855
856 // Check if we're upgrading TO 2.2.2 and haven't handled this yet
857 if (version_compare($current_version, $new_version, '<') && !$update_handled) {
858 $options = get_option('mxchat_options', array());
859
860 // Check if live agent was previously enabled
861 if (isset($options['live_agent_status']) && $options['live_agent_status'] === 'on') {
862 // Disable live agent
863 $options['live_agent_status'] = 'off';
864 update_option('mxchat_options', $options);
865
866 // Set flag to show the notification banner
867 update_option('mxchat_show_live_agent_disabled_notice', true);
868 }
869
870 // Mark this update as handled
871 update_option('mxchat_live_agent_update_2_2_2_handled', true);
872 }
873 }
874
875 // Initialize plugin safely
876 function mxchat_init() {
877 // Include all class files first
878 mxchat_include_classes();
879
880 // Run update check (this also ensures tables exist)
881 mxchat_check_for_update();
882
883 // CRITICAL: Ensure tables exist on admin pages (safety net)
884 add_action('admin_init', 'mxchat_ensure_tables_exist', 1);
885
886 // Add fallback rate limit check
887 add_action('init', 'mxchat_check_fallback_rate_limits', 5);
888
889 // Add migration notice hook
890 add_action('admin_notices', 'mxchat_show_migration_notice');
891
892 // Initialize classes with error handling
893 try {
894 // Initialize admin classes
895 if (is_admin()) {
896 if (class_exists('MxChat_Knowledge_Manager')) {
897 $mxchat_knowledge_manager = new MxChat_Knowledge_Manager();
898
899 if (class_exists('MxChat_Admin')) {
900 $mxchat_admin = new MxChat_Admin($mxchat_knowledge_manager);
901 }
902 }
903
904 // Initialize meta box class
905 if (class_exists('MxChat_Meta_Box')) {
906 new MxChat_Meta_Box();
907 }
908 }
909
910 // Initialize public classes
911 if (class_exists('MxChat_Public')) {
912 $mxchat_public = new MxChat_Public();
913 }
914
915 if (class_exists('MxChat_Integrator')) {
916 global $mxchat_integrator;
917 $mxchat_integrator = new MxChat_Integrator();
918 }
919
920 } catch (Exception $e) {
921 //error_log('MxChat initialization error: ' . $e->getMessage());
922
923 // Show admin notice if there's an error
924 if (is_admin()) {
925 add_action('admin_notices', function() use ($e) {
926 echo '<div class="notice notice-error"><p>';
927 echo '<strong>MxChat Error:</strong> Plugin initialization failed. ';
928 echo 'Please check error logs or contact support. Error: ' . esc_html($e->getMessage());
929 echo '</p></div>';
930 });
931 }
932 }
933 }
934
935 // Run initialization on plugins_loaded
936 add_action('plugins_loaded', 'mxchat_init');
937
938 // Run migration check on admin init (for auto-updates without reactivation)
939 add_action('admin_init', 'mxchat_check_and_run_migrations');
940
941 /**
942 * Check and run migrations on admin init
943 * This ensures migrations run even when plugin is auto-updated
944 */
945 function mxchat_check_and_run_migrations() {
946 // Only run in admin and not on every request
947 static $checked = false;
948 if ($checked) {
949 return;
950 }
951 $checked = true;
952
953 mxchat_migrate_pinecone_roles_add_bot_id();
954 mxchat_migrate_add_content_type_column();
955 }
956
957 // Register activation hook
958 register_activation_hook(__FILE__, 'mxchat_activate');
959
960 // Add cron schedule
961 add_filter('cron_schedules', function($schedules) {
962 $schedules['one_minute'] = array(
963 'interval' => 60,
964 'display' => 'Every Minute'
965 );
966 return $schedules;
967 });
968
969 // Register deactivation hook
970 register_deactivation_hook(__FILE__, 'mxchat_deactivate');