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

971 lines 34.5 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.9
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 wp_clear_scheduled_hook('mxchat_send_delayed_transcript');
633
634 // Clear fallback options
635 delete_option('mxchat_use_fallback_rate_limits');
636 delete_option('mxchat_next_rate_limit_check');
637 delete_option('mxchat_fallback_check_interval');
638
639 // NOTE: We do NOT delete queue tables on deactivation
640 // This preserves data if user accidentally deactivates the plugin
641 }
642
643 /**
644 * Check if fallback rate limit cleanup is needed
645 */
646 function mxchat_check_fallback_rate_limits() {
647 $use_fallback = get_option('mxchat_use_fallback_rate_limits', false);
648
649 if (!$use_fallback) {
650 return;
651 }
652
653 $next_check = get_option('mxchat_next_rate_limit_check', 0);
654
655 if (time() >= $next_check) {
656 // Only run reset if the MxChat_Integrator class exists
657 if (class_exists('MxChat_Integrator')) {
658 $integrator = new MxChat_Integrator();
659 if (method_exists($integrator, 'mxchat_reset_rate_limits')) {
660 $integrator->mxchat_reset_rate_limits();
661 update_option('mxchat_next_rate_limit_check', time() + 3600);
662 }
663 }
664 }
665 }
666
667 /**
668 * Robust update checking with role restriction migration, model deprecation, and queue tables
669 * CRITICAL: This runs on EVERY page load to ensure tables exist
670 */
671 function mxchat_check_for_update() {
672 global $wpdb;
673
674 try {
675 $current_version = get_option('mxchat_plugin_version', '0.0.0');
676 $plugin_version = MXCHAT_VERSION;
677
678 // Always ensure critical tables exist (even if version matches)
679 // This handles manual table deletion or fresh installs
680 $chat_table = $wpdb->prefix . 'mxchat_chat_transcripts';
681 $queue_table = $wpdb->prefix . 'mxchat_processing_queue';
682
683 $chat_exists = $wpdb->get_var("SHOW TABLES LIKE '$chat_table'") === $chat_table;
684 $queue_exists = $wpdb->get_var("SHOW TABLES LIKE '$queue_table'") === $queue_table;
685
686 if (!$chat_exists || !$queue_exists) {
687 //error_log("MxChat: Critical tables missing, running activation");
688 mxchat_activate();
689 }
690
691 // Version-specific migrations
692 if ($current_version !== $plugin_version) {
693 //error_log("MxChat: Version change detected: $current_version -> $plugin_version");
694
695 // Run live agent update BEFORE updating the stored version
696 mxchat_handle_live_agent_update();
697
698 // Run role restriction migration for 2.4.1
699 if (version_compare($current_version, '2.4.1', '<')) {
700 mxchat_add_role_restriction_column();
701 }
702
703 // Run enabled_bots column migration for 2.4.4
704 if (version_compare($current_version, '2.4.4', '<')) {
705 mxchat_add_enabled_bots_column();
706 }
707
708 // Run model migration for 2.5.1 (Claude deprecation)
709 if (version_compare($current_version, '2.5.1', '<')) {
710 mxchat_migrate_deprecated_models();
711 }
712
713 // 2.5.2: Ensure queue tables exist and fix URL column sizes for all users upgrading to 2.5.2
714 if (version_compare($current_version, '2.5.2', '<')) {
715 mxchat_create_queue_tables();
716 mxchat_fix_url_column_size(); // NEW: Fix URL column size for long URLs
717 //error_log("MxChat: Queue tables created and URL columns updated for upgrade to 2.5.2");
718 }
719
720 // Run full activation to ensure everything is up to date
721 mxchat_activate();
722
723 // Run migration functions
724 mxchat_migrate_live_agent_status();
725
726 // Add the cleanup function for version 2.1.8
727 if (version_compare($current_version, '2.1.8', '<')) {
728 $deleted = mxchat_cleanup_orphaned_chat_history();
729 }
730
731 // Update version LAST
732 update_option('mxchat_plugin_version', $plugin_version);
733
734 //error_log("MxChat: Updated from version $current_version to $plugin_version");
735 }
736
737 } catch (Exception $e) {
738 //error_log('MxChat update error: ' . $e->getMessage());
739 // Don't update version if there was an error
740 }
741 }
742
743 /**
744 * Ensure tables exist on every admin load for fresh installations
745 * This is a safety net for cases where activation hook doesn't fire
746 */
747 function mxchat_ensure_tables_exist() {
748 global $wpdb;
749
750 // Only run for admin users to avoid performance impact
751 if (!current_user_can('administrator')) {
752 return;
753 }
754
755 // Check if we've already verified tables in this session
756 static $tables_checked = false;
757 if ($tables_checked) {
758 return;
759 }
760 $tables_checked = true;
761
762 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
763 $queue_table = $wpdb->prefix . 'mxchat_processing_queue';
764
765 $chat_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
766 $queue_exists = $wpdb->get_var("SHOW TABLES LIKE '$queue_table'") === $queue_table;
767
768 if (!$chat_exists || !$queue_exists) {
769 //error_log("MxChat: Tables missing on admin load, running activation");
770 mxchat_activate();
771 }
772 }
773
774 /**
775 * Clean up orphaned chat history options from the wp_options table
776 * @return int Number of options deleted
777 */
778 function mxchat_cleanup_orphaned_chat_history() {
779 global $wpdb;
780 $count = 0;
781
782 // Get all option keys that match our pattern
783 $history_options = $wpdb->get_results(
784 "SELECT option_name FROM {$wpdb->options}
785 WHERE option_name LIKE 'mxchat_history_%'"
786 );
787
788 if (!empty($history_options)) {
789 foreach ($history_options as $option) {
790 // Extract the session ID from the option name
791 $session_id = str_replace('mxchat_history_', '', $option->option_name);
792
793 // Check if this session still exists in the custom table
794 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
795 $exists = $wpdb->get_var(
796 $wpdb->prepare(
797 "SELECT COUNT(*) FROM {$table_name} WHERE session_id = %s",
798 $session_id
799 )
800 );
801
802 // If session doesn't exist in the main table, delete the option
803 if ($exists == 0) {
804 delete_option($option->option_name);
805 // Also delete related metadata
806 delete_option("mxchat_email_{$session_id}");
807 delete_option("mxchat_name_{$session_id}");
808 delete_option("mxchat_agent_name_{$session_id}");
809 $count++;
810 }
811 }
812 }
813
814 return $count;
815 }
816
817 function mxchat_migrate_live_agent_status() {
818 $options = get_option('mxchat_options', []);
819
820 // Check if live_agent_status exists
821 if (isset($options['live_agent_status'])) {
822 $current_status = $options['live_agent_status'];
823 $needs_update = false;
824
825 // Convert to new format if needed
826 if ($current_status === 'online') {
827 $options['live_agent_status'] = 'on';
828 $needs_update = true;
829 } else if ($current_status === 'offline') {
830 $options['live_agent_status'] = 'off';
831 $needs_update = true;
832 } else if (!in_array($current_status, ['on', 'off'])) {
833 // Default to off for any unexpected values
834 $options['live_agent_status'] = 'off';
835 $needs_update = true;
836 }
837
838 // Only update if needed
839 if ($needs_update) {
840 update_option('mxchat_options', $options);
841 }
842 } else {
843 // If status doesn't exist, set default to off
844 $options['live_agent_status'] = 'off';
845 update_option('mxchat_options', $options);
846 }
847 }
848
849 function mxchat_handle_live_agent_update() {
850 // Get the CURRENT stored version (before it gets updated)
851 $current_version = get_option('mxchat_plugin_version', '0.0.0');
852 $new_version = '2.2.2';
853
854 // Only run this once for the update to 2.2.2
855 $update_handled = get_option('mxchat_live_agent_update_2_2_2_handled', false);
856
857 // Check if we're upgrading TO 2.2.2 and haven't handled this yet
858 if (version_compare($current_version, $new_version, '<') && !$update_handled) {
859 $options = get_option('mxchat_options', array());
860
861 // Check if live agent was previously enabled
862 if (isset($options['live_agent_status']) && $options['live_agent_status'] === 'on') {
863 // Disable live agent
864 $options['live_agent_status'] = 'off';
865 update_option('mxchat_options', $options);
866
867 // Set flag to show the notification banner
868 update_option('mxchat_show_live_agent_disabled_notice', true);
869 }
870
871 // Mark this update as handled
872 update_option('mxchat_live_agent_update_2_2_2_handled', true);
873 }
874 }
875
876 // Initialize plugin safely
877 function mxchat_init() {
878 // Include all class files first
879 mxchat_include_classes();
880
881 // Run update check (this also ensures tables exist)
882 mxchat_check_for_update();
883
884 // CRITICAL: Ensure tables exist on admin pages (safety net)
885 add_action('admin_init', 'mxchat_ensure_tables_exist', 1);
886
887 // Add fallback rate limit check
888 add_action('init', 'mxchat_check_fallback_rate_limits', 5);
889
890 // Add migration notice hook
891 add_action('admin_notices', 'mxchat_show_migration_notice');
892
893 // Initialize classes with error handling
894 try {
895 // Initialize admin classes
896 if (is_admin()) {
897 if (class_exists('MxChat_Knowledge_Manager')) {
898 $mxchat_knowledge_manager = new MxChat_Knowledge_Manager();
899
900 if (class_exists('MxChat_Admin')) {
901 $mxchat_admin = new MxChat_Admin($mxchat_knowledge_manager);
902 }
903 }
904
905 // Initialize meta box class
906 if (class_exists('MxChat_Meta_Box')) {
907 new MxChat_Meta_Box();
908 }
909 }
910
911 // Initialize public classes
912 if (class_exists('MxChat_Public')) {
913 $mxchat_public = new MxChat_Public();
914 }
915
916 if (class_exists('MxChat_Integrator')) {
917 global $mxchat_integrator;
918 $mxchat_integrator = new MxChat_Integrator();
919 }
920
921 } catch (Exception $e) {
922 //error_log('MxChat initialization error: ' . $e->getMessage());
923
924 // Show admin notice if there's an error
925 if (is_admin()) {
926 add_action('admin_notices', function() use ($e) {
927 echo '<div class="notice notice-error"><p>';
928 echo '<strong>MxChat Error:</strong> Plugin initialization failed. ';
929 echo 'Please check error logs or contact support. Error: ' . esc_html($e->getMessage());
930 echo '</p></div>';
931 });
932 }
933 }
934 }
935
936 // Run initialization on plugins_loaded
937 add_action('plugins_loaded', 'mxchat_init');
938
939 // Run migration check on admin init (for auto-updates without reactivation)
940 add_action('admin_init', 'mxchat_check_and_run_migrations');
941
942 /**
943 * Check and run migrations on admin init
944 * This ensures migrations run even when plugin is auto-updated
945 */
946 function mxchat_check_and_run_migrations() {
947 // Only run in admin and not on every request
948 static $checked = false;
949 if ($checked) {
950 return;
951 }
952 $checked = true;
953
954 mxchat_migrate_pinecone_roles_add_bot_id();
955 mxchat_migrate_add_content_type_column();
956 }
957
958 // Register activation hook
959 register_activation_hook(__FILE__, 'mxchat_activate');
960
961 // Add cron schedule
962 add_filter('cron_schedules', function($schedules) {
963 $schedules['one_minute'] = array(
964 'interval' => 60,
965 'display' => 'Every Minute'
966 );
967 return $schedules;
968 });
969
970 // Register deactivation hook
971 register_deactivation_hook(__FILE__, 'mxchat_deactivate');