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

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