PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.6.0
MxChat – AI Chatbot & Content Generation for WordPress v2.6.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.6.0, at mxchat-basic.php

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