PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.0.2
MxChat – AI Chatbot & Content Generation for WordPress v3.0.2
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 3.0.2, at mxchat-basic.php

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