PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.3
MxChat – AI Chatbot & Content Generation for WordPress v3.2.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
← All changes | mxchat-basic.php +60 -348 3.2.143.2.3 View file →
@@ -2,9 +2,9 @@
2 2 /**
3 3 * Plugin Name: MxChat
4 4 * Plugin URI: https://mxchat.ai/
5 5 * Description: AI chatbot for WordPress with OpenAI, Claude, xAI, DeepSeek, live agent, PDF uploads, WooCommerce, and training on website data.
6 - * Version: 3.2.14
6 + * Version: 3.2.3
7 7 * Author: MxChat
8 8 * Author URI: https://mxchat.ai
9 9 * License: GPLv2 or later
10 10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -29,27 +29,8 @@
29 29 }
30 30 define('MXCHAT_VERSION', $version);
31 31 }
32 32
33 -/**
34 - * Honest, versioned User-Agent for MXChat remote-content ingestion fetches
35 - * (Knowledge Base PDF import, URL import, sitemap / website crawl).
36 - *
37 - * WAF rulesets (SiteGround/ModSecurity, Wordfence, Cloudflare managed rules)
38 - * flag stale spoofed-browser UAs as scrapers and return 403 — which silently
39 - * broke the single most common KB source: self-hosted media on the site's own
40 - * domain. A truthful crawler identifier is the industry norm for well-behaved
41 - * bots and lets a site owner allowlist "MXChatBot" in their WAF. Filterable so
42 - * a locked-down host can supply a different string without a code change.
43 - */
44 -if (!function_exists('mxchat_ingest_user_agent')) {
45 - function mxchat_ingest_user_agent() {
46 - $version = defined('MXCHAT_VERSION') ? MXCHAT_VERSION : '1.0';
47 - $ua = 'MXChatBot/' . $version . ' (+https://mxchat.ai/bot)';
48 - return apply_filters('mxchat_ingest_user_agent', $ua);
49 - }
50 -}
51 -
52 33 function mxchat_load_textdomain() {
53 34 $domain = 'mxchat';
54 35 $locale = determine_locale();
55 36
@@ -65,84 +46,8 @@
65 46 }
66 47 add_action('init', 'mxchat_load_textdomain');
67 48
68 49 /**
69 - * One-time migration: gemini-3-pro-preview was shut down by Google on March 9, 2026.
70 - * Existing installs with the dead ID get auto-remapped to gemini-3.1-pro-preview
71 - * (Google's official migration target) the first time admin_init fires after update.
72 - */
73 -add_action('admin_init', function () {
74 - if (get_option('mxchat_gemini_3_remap_done')) {
75 - return;
76 - }
77 - $opts = get_option('mxchat_options');
78 - if (is_array($opts) && isset($opts['model']) && $opts['model'] === 'gemini-3-pro-preview') {
79 - $opts['model'] = 'gemini-3.1-pro-preview';
80 - update_option('mxchat_options', $opts);
81 - }
82 - if (is_array($opts) && isset($opts['content_model']) && $opts['content_model'] === 'gemini-3-pro-preview') {
83 - $opts['content_model'] = 'gemini-3.1-pro-preview';
84 - update_option('mxchat_options', $opts);
85 - }
86 - update_option('mxchat_gemini_3_remap_done', 1);
87 -});
88 -
89 -/**
90 - * One-time migration: the Grok 2 family was retired by xAI (grok-2, grok-2-1212,
91 - * grok-2-latest, grok-2-vision-1212 all return 400 "Model not found"). Existing
92 - * installs with the dead ID get auto-remapped to grok-4-1-fast-non-reasoning
93 - * (modern, fast, broadly available) the first time admin_init fires after update.
94 - */
95 -add_action('admin_init', function () {
96 - if (get_option('mxchat_grok_2_remap_done')) {
97 - return;
98 - }
99 - $opts = get_option('mxchat_options');
100 - if (is_array($opts) && isset($opts['model']) && $opts['model'] === 'grok-2') {
101 - $opts['model'] = 'grok-4-1-fast-non-reasoning';
102 - update_option('mxchat_options', $opts);
103 - }
104 - if (is_array($opts) && isset($opts['content_model']) && $opts['content_model'] === 'grok-2') {
105 - $opts['content_model'] = 'grok-4-1-fast-non-reasoning';
106 - update_option('mxchat_options', $opts);
107 - }
108 - update_option('mxchat_grok_2_remap_done', 1);
109 -});
110 -
111 -/**
112 - * One-time migration: Anthropic retired the Claude 4 (2025-05-14) snapshots on
113 - * June 15, 2026 — claude-opus-4-20250514 and claude-sonnet-4-20250514 now return
114 - * an API error. Existing installs with a dead ID get auto-remapped to the current
115 - * equivalents Anthropic recommends (Opus 4.8 / Sonnet 4.6) the first time admin_init
116 - * fires after update. Mirrors the gemini-3-pro-preview / grok-2 rescues above.
117 - */
118 -add_action('admin_init', function () {
119 - if (get_option('mxchat_claude_4_retire_remap_done')) {
120 - return;
121 - }
122 - $map = array(
123 - 'claude-opus-4-20250514' => 'claude-opus-4-8',
124 - 'claude-sonnet-4-20250514' => 'claude-sonnet-4-6',
125 - );
126 - $opts = get_option('mxchat_options');
127 - if (is_array($opts)) {
128 - $changed = false;
129 - if (isset($opts['model']) && isset($map[$opts['model']])) {
130 - $opts['model'] = $map[$opts['model']];
131 - $changed = true;
132 - }
133 - if (isset($opts['content_model']) && isset($map[$opts['content_model']])) {
134 - $opts['content_model'] = $map[$opts['content_model']];
135 - $changed = true;
136 - }
137 - if ($changed) {
138 - update_option('mxchat_options', $opts);
139 - }
140 - }
141 - update_option('mxchat_claude_4_retire_remap_done', 1);
142 -});
143 -
144 -/**
145 50 * Exclude MxChat assets from caching plugin optimizations
146 51 *
147 52 * This prevents issues with WP Rocket, LiteSpeed Cache, Autoptimize, WP Super Cache,
148 53 * W3 Total Cache, SG Optimizer, and similar plugins that may break the chatbot by
@@ -345,67 +250,11 @@
345 250 $rejected[] = 'wp-admin/admin-ajax.php';
346 251 return $rejected;
347 252 });
348 253
349 -// ── Page-cache bypass for chat AJAX (companion to the 3.2.6 nonce-race hotfix)
350 -// Each cache plugin gets its own filter export so that visitors hitting an
351 -// edge-cached page never receive cached chat-AJAX responses. The chat send /
352 -// stream send / file upload all POST to /wp-admin/admin-ajax.php with
353 -// `action=mxchat_*`. Without these exports, a cache plugin can stale a response
354 -// and break the per-session nonce flow on the first message.
355 -
356 -// WP Rocket — `rocket_cache_reject_uri` takes a flat array of regex strings.
357 -add_filter('rocket_cache_reject_uri', function($uris) {
358 - if (!is_array($uris)) $uris = array();
359 - $uris[] = '/wp-admin/admin-ajax\.php\?action=mxchat_.*';
360 - return $uris;
361 -});
362 -
363 -// LiteSpeed Cache — `litespeed_cache_no_cache_for_request` short-circuits
364 -// caching when the request matches our chat-AJAX pattern.
365 -add_filter('litespeed_cache_no_cache_for_request', function($no_cache) {
366 - if ($no_cache) return $no_cache;
367 - if (!empty($_SERVER['REQUEST_URI']) &&
368 - strpos($_SERVER['REQUEST_URI'], '/wp-admin/admin-ajax.php') !== false &&
369 - !empty($_REQUEST['action']) &&
370 - strpos((string) $_REQUEST['action'], 'mxchat_') === 0) {
371 - return true;
372 - }
373 - return $no_cache;
374 -});
375 -
376 -// W3 Total Cache — `w3tc_pgcache_request_skip_uri` flips page-cache off when
377 -// the URI matches.
378 -add_filter('w3tc_pgcache_request_skip_uri', function($skip) {
379 - if ($skip) return $skip;
380 - if (!empty($_SERVER['REQUEST_URI']) &&
381 - strpos($_SERVER['REQUEST_URI'], '/wp-admin/admin-ajax.php') !== false &&
382 - !empty($_REQUEST['action']) &&
383 - strpos((string) $_REQUEST['action'], 'mxchat_') === 0) {
384 - return true;
385 - }
386 - return $skip;
387 -});
388 -
389 -// FlyingPress — `flying_press_cacheable` takes a boolean and is run per
390 -// request. Same pattern as LiteSpeed / W3TC.
391 -add_filter('flying_press_cacheable', function($cacheable) {
392 - if (!$cacheable) return $cacheable;
393 - if (!empty($_SERVER['REQUEST_URI']) &&
394 - strpos($_SERVER['REQUEST_URI'], '/wp-admin/admin-ajax.php') !== false &&
395 - !empty($_REQUEST['action']) &&
396 - strpos((string) $_REQUEST['action'], 'mxchat_') === 0) {
397 - return false;
398 - }
399 - return $cacheable;
400 -});
401 -
402 254 // Include classes with error handling
403 255 function mxchat_include_classes() {
404 256 $class_files = array(
405 - 'includes/class-mxchat-model-catalog.php',
406 - 'includes/class-mxchat-live-agent-schedule.php',
407 - 'includes/class-mxchat-tool-registry.php',
408 257 'includes/class-mxchat-integrator.php',
409 258 'includes/class-mxchat-admin.php',
410 259 'includes/class-mxchat-public.php',
411 260 'includes/class-mxchat-utils.php',
@@ -413,11 +262,8 @@
413 262 'includes/class-mxchat-meta-box.php',
414 263 'includes/class-mxchat-chunker.php',
415 264 'includes/class-mxchat-word-handler.php',
416 265 'includes/class-mxchat-content-generator.php',
417 - 'includes/class-mxchat-cache-purge.php',
418 - 'includes/class-mxchat-editor-assistant.php',
419 - 'includes/class-rest-api.php',
420 266 'admin/class-ajax-handler.php',
421 267 'admin/class-pinecone-manager.php',
422 268 'admin/class-knowledge-manager.php'
423 269 );
@@ -429,38 +275,8 @@
429 275 } else {
430 276 //error_log('MxChat: Missing class file - ' . $file);
431 277 }
432 278 }
433 -
434 - // Register the native function-calling admin-post save handler (a41dee).
435 - if (class_exists('MxChat_Tool_Registry')) {
436 - MxChat_Tool_Registry::init();
437 - }
438 -
439 - // Editor Assistant — free, OFF-by-default block-editor AI actions (plan-8cb0cb).
440 - // init() wires REST + streaming AJAX + sidebar enqueue ONLY when the
441 - // mxchat_editor_assistant_enabled option is 'on'; otherwise zero footprint.
442 - if (class_exists('MxChat_Editor_Assistant')) {
443 - MxChat_Editor_Assistant::init();
444 - }
445 -
446 - // Admin pages that aren't classes (procedural include).
447 - if (is_admin()) {
448 - $admin_api_page = plugin_dir_path(__FILE__) . 'includes/admin-api-page.php';
449 - if (file_exists($admin_api_page)) {
450 - require_once $admin_api_page;
451 - }
452 - // f7c7d4 renamed this file admin-dashboard-page.php → admin-onboarding-page.php.
453 - // The require MUST live here (admin bootstrap) and not just inside
454 - // mxchat_add_plugin_page() on the admin_menu hook — admin_menu does NOT
455 - // fire on admin-ajax.php requests, so the wizard's AJAX handlers
456 - // (plan-905439: mxchat_onboarding_kb_status / save_step / mark_step /
457 - // auto_graduate + the f7c7d4 dismiss handler) would never register.
458 - $admin_onboarding_page = plugin_dir_path(__FILE__) . 'includes/admin-onboarding-page.php';
459 - if (file_exists($admin_onboarding_page)) {
460 - require_once $admin_onboarding_page;
461 - }
462 - }
463 279 }
464 280
465 281 /**
466 282 * Lazy-load the PDF parser library only when needed.
@@ -792,30 +608,51 @@
792 608 update_option('mxchat_content_type_migration_version', '2.5.6');
793 609 }
794 610
795 611 /**
796 - * 3.2.4: Backfill the active embedding model option for installs that already
797 - * have KB content but no stamped model. The mismatch warning compares this
798 - * against the user's currently selected model — no per-row column needed.
612 + * 3.2.3: Add embedding_model column to KB and intent tables so we can detect
613 + * when the active embedding model is changed while content is already embedded.
799 614 */
800 -function mxchat_backfill_active_embedding_model() {
615 +function mxchat_add_embedding_model_columns() {
801 616 global $wpdb;
802 617
803 - if (get_option('mxchat_active_embedding_model', '') !== '') {
618 + $migration_version = get_option('mxchat_embedding_model_migration_version', '0');
619 + if (version_compare($migration_version, '3.2.3', '>=')) {
804 620 return;
805 621 }
806 622
807 - $kb_table = $wpdb->prefix . 'mxchat_system_prompt_content';
808 - if ($wpdb->get_var("SHOW TABLES LIKE '{$kb_table}'") !== $kb_table) {
809 - return;
623 + $tables = array(
624 + $wpdb->prefix . 'mxchat_system_prompt_content',
625 + $wpdb->prefix . 'mxchat_intents',
626 + $wpdb->prefix . 'mxchat_intent_phrases',
627 + );
628 +
629 + foreach ($tables as $table) {
630 + if ($wpdb->get_var("SHOW TABLES LIKE '{$table}'") !== $table) {
631 + continue;
632 + }
633 + $col_exists = $wpdb->get_results("SHOW COLUMNS FROM {$table} LIKE 'embedding_model'");
634 + if (empty($col_exists)) {
635 + $wpdb->query("ALTER TABLE {$table} ADD COLUMN embedding_model VARCHAR(64) DEFAULT NULL");
636 + }
810 637 }
811 638
812 - $kb_count = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$kb_table}");
813 - if ($kb_count > 0) {
814 - $options = get_option('mxchat_options', array());
815 - $current_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
816 - update_option('mxchat_active_embedding_model', $current_model, false);
639 + // Best-effort backfill: if any existing content lacks a stamped model, assume
640 + // it was embedded with whatever model is currently selected. This is the
641 + // safest assumption for installations upgrading to 3.2.3 — if it was wrong
642 + // already, the mismatch detection will surface it on the next switch.
643 + $options = get_option('mxchat_options', array());
644 + $current_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
645 +
646 + if (get_option('mxchat_active_embedding_model', '') === '') {
647 + $kb_table = $wpdb->prefix . 'mxchat_system_prompt_content';
648 + $kb_count = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$kb_table}");
649 + if ($kb_count > 0) {
650 + update_option('mxchat_active_embedding_model', $current_model, false);
651 + }
817 652 }
653 +
654 + update_option('mxchat_embedding_model_migration_version', '3.2.3');
818 655 }
819 656
820 657 /**
821 658 * 2.5.2: Create queue processing tables for reliable background processing
@@ -890,34 +727,8 @@
890 727 dbDelta($sql);
891 728 }
892 729
893 730 /**
894 - * Create per-session satisfaction ratings table (v3.2.6)
895 - * Stores one 👍/👎 rating + optional feedback per chat session.
896 - */
897 -function mxchat_create_session_ratings_table() {
898 - global $wpdb;
899 - $charset_collate = $wpdb->get_charset_collate();
900 -
901 - $table_name = $wpdb->prefix . 'mxchat_session_ratings';
902 - $sql = "CREATE TABLE $table_name (
903 - id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
904 - session_id varchar(255) NOT NULL,
905 - bot_id varchar(50) NOT NULL DEFAULT 'default',
906 - rating_value tinyint(1) NOT NULL,
907 - rating_feedback text DEFAULT NULL,
908 - created_at datetime NOT NULL,
909 - PRIMARY KEY (id),
910 - UNIQUE KEY session_id (session_id),
911 - KEY bot_id (bot_id),
912 - KEY created_at (created_at)
913 - ) $charset_collate;";
914 -
915 - require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
916 - dbDelta($sql);
917 -}
918 -
919 -/**
920 731 * 2.5.2: Fix URL column size to support long URLs (especially with UTF-8 encoding)
921 732 * This fixes "url, source_url. The supplied values may be too long" errors
922 733 */
923 734 function mxchat_fix_url_column_size() {
@@ -1053,8 +864,9 @@
1053 864 embedding_vector LONGTEXT,
1054 865 source_url TEXT DEFAULT NULL,
1055 866 role_restriction VARCHAR(50) DEFAULT 'public',
1056 867 content_type VARCHAR(50) DEFAULT 'content',
868 + embedding_model VARCHAR(64) DEFAULT NULL,
1057 869 timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
1058 870 PRIMARY KEY (id),
1059 871 KEY content_type (content_type)
1060 872 ) $charset_collate;";
@@ -1069,8 +881,9 @@
1069 881 callback_function VARCHAR(255) NOT NULL,
1070 882 similarity_threshold FLOAT DEFAULT 0.85,
1071 883 enabled TINYINT(1) NOT NULL DEFAULT 1,
1072 884 enabled_bots LONGTEXT DEFAULT NULL,
885 + embedding_model VARCHAR(64) DEFAULT NULL,
1073 886 PRIMARY KEY (id)
1074 887 ) $charset_collate;";
1075 888
1076 889 // Individual Intent Phrases Table - each phrase gets its own embedding vector
@@ -1079,8 +892,9 @@
1079 892 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
1080 893 intent_id BIGINT(20) UNSIGNED NOT NULL,
1081 894 phrase TEXT NOT NULL,
1082 895 embedding_vector LONGTEXT NOT NULL,
896 + embedding_model VARCHAR(64) DEFAULT NULL,
1083 897 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
1084 898 PRIMARY KEY (id),
1085 899 KEY intent_id (intent_id)
1086 900 ) $charset_collate;";
@@ -1103,11 +917,8 @@
1103 917
1104 918 // Create transcript translations table
1105 919 mxchat_create_translations_table();
1106 920
1107 - // Create per-session satisfaction ratings table (v3.2.6)
1108 - mxchat_create_session_ratings_table();
1109 -
1110 921 // Ensure additional columns in system prompt table
1111 922 $existing_system_columns = $wpdb->get_results("SHOW COLUMNS FROM $system_prompt_table");
1112 923 if (!empty($existing_system_columns)) {
1113 924 $existing_system_column_names = array_column($existing_system_columns, 'Field');
@@ -1150,10 +961,10 @@
1150 961
1151 962 // Run migration for existing installations
1152 963 mxchat_migrate_pinecone_roles_add_bot_id();
1153 964
1154 - // 3.2.4: Backfill active embedding model option (replaces 3.2.3 column-based tracking)
1155 - mxchat_backfill_active_embedding_model();
965 + // 3.2.3: Add embedding_model column to KB and intent tables
966 + mxchat_add_embedding_model_columns();
1156 967
1157 968 // Setup cron jobs
1158 969 mxchat_setup_cron_jobs();
1159 970
@@ -1174,35 +985,28 @@
1174 985 if (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) {
1175 986 // Set flag to use fallback system
1176 987 update_option('mxchat_use_fallback_rate_limits', true);
1177 988 update_option('mxchat_next_rate_limit_check', time() + 3600);
1178 - // Deliberately NO early return (plan-bc08a6): transcript cleanup below
1179 - // must still be scheduled. DISABLE_WP_CRON only changes HOW cron events
1180 - // execute (a server-side runner hitting wp-cron.php instead of loopback
1181 - // spawns) — scheduling still just writes the cron option. The old early
1182 - // return here meant a deactivate/reactivate cycle on a DISABLE_WP_CRON
1183 - // site permanently lost the transcript cleanup event while the retention
1184 - // setting still claimed to be active.
989 + return;
990 + }
991 +
992 + // Schedule the rate limit reset cron job
993 + $result = wp_schedule_event(time() + 300, 'hourly', 'mxchat_reset_rate_limits');
994 +
995 + if ($result === false) {
996 + // Fallback if scheduling fails
997 + update_option('mxchat_use_fallback_rate_limits', true);
998 + update_option('mxchat_next_rate_limit_check', time() + 3600);
1185 999 } else {
1186 - // Schedule the rate limit reset cron job
1187 - $result = wp_schedule_event(time() + 300, 'hourly', 'mxchat_reset_rate_limits');
1188 -
1189 - if ($result === false) {
1190 - // Fallback if scheduling fails
1191 - update_option('mxchat_use_fallback_rate_limits', true);
1192 - update_option('mxchat_next_rate_limit_check', time() + 3600);
1193 - } else {
1194 - // Clear fallback flags if cron scheduling succeeded
1195 - delete_option('mxchat_use_fallback_rate_limits');
1196 - }
1000 + // Clear fallback flags if cron scheduling succeeded
1001 + delete_option('mxchat_use_fallback_rate_limits');
1197 1002 }
1198 -
1199 - // Schedule transcript cleanup if configured (bucket dropdown OR custom retention-days > 0)
1003 +
1004 + // Schedule transcript cleanup if configured
1200 1005 $transcript_options = get_option('mxchat_transcripts_options', array());
1201 1006 $cleanup_interval = isset($transcript_options['mxchat_auto_delete_transcripts']) ? $transcript_options['mxchat_auto_delete_transcripts'] : 'never';
1202 - $custom_retention = isset($transcript_options['mxchat_retention_days']) ? (int) $transcript_options['mxchat_retention_days'] : 0;
1203 -
1204 - if ($cleanup_interval !== 'never' || $custom_retention > 0) {
1007 +
1008 + if ($cleanup_interval !== 'never') {
1205 1009 // Check if not already scheduled
1206 1010 if (!wp_next_scheduled('mxchat_cleanup_old_transcripts')) {
1207 1011 // Schedule to run daily at 3 AM
1208 1012 $next_run = strtotime('tomorrow 3:00 AM');
@@ -1336,12 +1140,11 @@
1336 1140 delete_option('mxchat_email_null');
1337 1141 delete_option('mxchat_name_null');
1338 1142 }
1339 1143
1340 - // 3.2.4: Backfill active embedding model option for the warning UI
1341 - // (replaces the per-row column tracking from 3.2.3, which was reverted)
1342 - if (version_compare($current_version, '3.2.4', '<')) {
1343 - mxchat_backfill_active_embedding_model();
1144 + // 3.2.3: Add embedding_model tracking columns and backfill active model
1145 + if (version_compare($current_version, '3.2.3', '<')) {
1146 + mxchat_add_embedding_model_columns();
1344 1147 }
1345 1148
1346 1149 // Run full activation to ensure everything is up to date
1347 1150 mxchat_activate();
@@ -1568,23 +1371,8 @@
1568 1371 if (class_exists('MxChat_Content_Generator')) {
1569 1372 new MxChat_Content_Generator();
1570 1373 }
1571 1374
1572 - // Initialize cache purge globally — settings writes can happen on any
1573 - // request type (admin screens, admin-ajax autosave, wp-cli), and the
1574 - // deferred-purge cron event fires on front-end requests.
1575 - if (class_exists('MxChat_Cache_Purge')) {
1576 - MxChat_Cache_Purge::init();
1577 - }
1578 -
1579 - // Initialize REST API globally — endpoints must be registered on
1580 - // every request (admin and frontend) so they're reachable via /wp-json/.
1581 - // Endpoints are auth-gated and locked until the site owner generates
1582 - // a token in MxChat → API Access.
1583 - if (class_exists('MxChat_Rest_Api')) {
1584 - new MxChat_Rest_Api();
1585 - }
1586 -
1587 1375 // Initialize public classes
1588 1376 if (class_exists('MxChat_Public')) {
1589 1377 $mxchat_public = new MxChat_Public();
1590 1378 }
@@ -1629,25 +1417,11 @@
1629 1417
1630 1418 mxchat_migrate_pinecone_roles_add_bot_id();
1631 1419 mxchat_migrate_add_content_type_column();
1632 1420 mxchat_migrate_add_translations_table();
1633 - mxchat_migrate_add_session_ratings_table();
1634 1421 }
1635 1422
1636 1423 /**
1637 - * Migration: Create per-session satisfaction ratings table (v3.2.6)
1638 - * For users upgrading from versions before 3.2.6
1639 - */
1640 -function mxchat_migrate_add_session_ratings_table() {
1641 - $migration_key = 'mxchat_session_ratings_table_created';
1642 - if (get_option($migration_key)) {
1643 - return;
1644 - }
1645 - mxchat_create_session_ratings_table();
1646 - update_option($migration_key, '3.2.6');
1647 -}
1648 -
1649 -/**
1650 1424 * Migration: Create transcript translations table (v3.0.4)
1651 1425 * For users upgrading from versions before 3.0.4
1652 1426 */
1653 1427 function mxchat_migrate_add_translations_table() {
@@ -1690,67 +1464,5 @@
1690 1464 return $schedules;
1691 1465 });
1692 1466
1693 1467 // Register deactivation hook
1694 -register_deactivation_hook(__FILE__, 'mxchat_deactivate');
1695 -
1696 -/**
1697 - * Per-session satisfaction rating: AJAX save handler (v3.2.6).
1698 - * Records one 👍/👎 + optional feedback per chat session. The UNIQUE KEY on
1699 - * session_id makes this naturally idempotent — only the first rating per
1700 - * session is stored; duplicate POSTs are silent no-ops.
1701 - */
1702 -function mxchat_save_session_rating() {
1703 - global $wpdb;
1704 -
1705 - $session_id = isset($_POST['session_id']) ? sanitize_text_field(wp_unslash($_POST['session_id'])) : '';
1706 - $bot_id = isset($_POST['bot_id']) ? sanitize_text_field(wp_unslash($_POST['bot_id'])) : 'default';
1707 - $rating_raw = isset($_POST['rating']) ? (int) $_POST['rating'] : 0;
1708 - $feedback = isset($_POST['feedback']) ? sanitize_textarea_field(wp_unslash($_POST['feedback'])) : '';
1709 -
1710 - if ($session_id === '' || ($rating_raw !== 1 && $rating_raw !== -1)) {
1711 - wp_send_json_error(array('message' => 'invalid_input'), 400);
1712 - }
1713 -
1714 - if (strlen($feedback) > 1000) {
1715 - $feedback = substr($feedback, 0, 1000);
1716 - }
1717 -
1718 - $table_name = $wpdb->prefix . 'mxchat_session_ratings';
1719 - $existing = $wpdb->get_var($wpdb->prepare(
1720 - "SELECT id FROM $table_name WHERE session_id = %s LIMIT 1",
1721 - $session_id
1722 - ));
1723 -
1724 - if ($existing) {
1725 - if ($feedback !== '') {
1726 - $wpdb->update(
1727 - $table_name,
1728 - array('rating_feedback' => $feedback),
1729 - array('id' => (int) $existing),
1730 - array('%s'),
1731 - array('%d')
1732 - );
1733 - }
1734 - wp_send_json_success(array('updated' => true));
1735 - }
1736 -
1737 - $inserted = $wpdb->insert(
1738 - $table_name,
1739 - array(
1740 - 'session_id' => $session_id,
1741 - 'bot_id' => $bot_id !== '' ? $bot_id : 'default',
1742 - 'rating_value' => $rating_raw,
1743 - 'rating_feedback' => $feedback !== '' ? $feedback : null,
1744 - 'created_at' => current_time('mysql'),
1745 - ),
1746 - array('%s', '%s', '%d', '%s', '%s')
1747 - );
1748 -
1749 - if ($inserted === false) {
1750 - wp_send_json_error(array('message' => 'db_insert_failed'), 500);
1751 - }
1752 -
1753 - wp_send_json_success(array('saved' => true));
1754 -}
1755 -add_action('wp_ajax_mxchat_save_rating', 'mxchat_save_session_rating');
1756 -add_action('wp_ajax_nopriv_mxchat_save_rating', 'mxchat_save_session_rating');
1468 +register_deactivation_hook(__FILE__, 'mxchat_deactivate');