PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.1.5
MxChat – AI Chatbot & Content Generation for WordPress v3.1.5
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 | admin/class-knowledge-manager.php +26 -101 3.2.13.1.5 View file →
@@ -163,13 +163,9 @@
163 163 public function mxchat_is_pdf_url($url, $response) {
164 164 $content_type = wp_remote_retrieve_header($response, 'content-type');
165 165 $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
166 166
167 - // Check Content-Disposition header for .pdf filename (Google Drive sends this)
168 - $disposition = wp_remote_retrieve_header($response, 'content-disposition');
169 - $has_pdf_disposition = ! empty($disposition) && stripos($disposition, '.pdf') !== false;
170 -
171 - return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf' || $has_pdf_disposition;
167 + return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf';
172 168 }
173 169
174 170
175 171 public function mxchat_handle_pdf_for_knowledge_base($pdf_url, $response, $bot_id = 'default') {
@@ -863,17 +859,10 @@
863 859 }
864 860
865 861 // For manual entries (no source_url or mxchat:// prefix), delete the old entry by ID first
866 862 // so submit_content_to_db creates a replacement instead of a duplicate
867 - // Also treat legacy mxchat.ai source URLs as manual — old bug assigned the site URL to manual entries
868 - $is_legacy_manual = !empty($source_url) && strpos($source_url, 'mxchat.ai') !== false && strpos($source_url, 'mxchat://') !== 0;
869 - if ( $entry_id > 0 && (empty($source_url) || strpos($source_url, 'mxchat://') === 0 || $is_legacy_manual) ) {
863 + if ( $entry_id > 0 && (empty($source_url) || strpos($source_url, 'mxchat://') === 0) ) {
870 864 $wpdb->delete( $table, array( 'id' => $entry_id ), array( '%d' ) );
871 - // Clear legacy URL so submit_content_to_db generates a unique mxchat:// identifier
872 - // instead of reusing the shared URL (which would mass-delete other entries with the same URL)
873 - if ( $is_legacy_manual ) {
874 - $source_url = '';
875 - }
876 865 }
877 866
878 867 // Use the existing submit_content_to_db which handles chunking, Pinecone, and WP DB
879 868 $vector_id = ! empty($source_url) ? md5($source_url) : md5('mxchat_manual_' . $entry_id);
@@ -950,22 +939,9 @@
950 939 exit;
951 940 }
952 941
953 942 $submitted_url = esc_url_raw($_POST['sitemap_url']);
954 -
955 - // Convert Google Drive sharing URLs to direct download URLs
956 - if ( strpos($submitted_url, 'drive.google.com') !== false ) {
957 - $file_id = '';
958 - if ( preg_match('/[?&]id=([a-zA-Z0-9_-]+)/', $submitted_url, $m) ) {
959 - $file_id = $m[1];
960 - } elseif ( preg_match('#/file/d/([a-zA-Z0-9_-]+)#', $submitted_url, $m) ) {
961 - $file_id = $m[1];
962 - }
963 - if ( ! empty($file_id) ) {
964 - $submitted_url = 'https://drive.google.com/uc?export=download&id=' . $file_id;
965 - }
966 - }
967 -
943 +
968 944 // Get bot_id from form submission
969 945 $bot_id = isset($_POST['bot_id']) ? sanitize_key($_POST['bot_id']) : 'default';
970 946
971 947 // Get bot-specific options and validate API key
@@ -993,18 +969,10 @@
993 969 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
994 970 exit;
995 971 }
996 972
997 - // Fetch URL — use browser-like headers so servers with bot protection don't block us
998 - $response = wp_remote_get($submitted_url, array(
999 - 'timeout' => 30,
1000 - 'sslverify' => false,
1001 - 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
1002 - 'headers' => array(
1003 - 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
1004 - 'Accept-Language' => 'en-US,en;q=0.9',
1005 - ),
1006 - ));
973 + // Fetch URL
974 + $response = wp_remote_get($submitted_url, array('timeout' => 30));
1007 975
1008 976 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
1009 977 $error_message = is_wp_error($response) ? $response->get_error_message() : 'HTTP Status: ' . wp_remote_retrieve_response_code($response);
1010 978 set_transient('mxchat_admin_notice_error',
@@ -2838,12 +2806,11 @@
2838 2806 foreach ($primary_indexes as $path => $source) {
2839 2807 $url = trailingslashit($site_url) . $path;
2840 2808
2841 2809 $response = wp_remote_head($url, array(
2842 - 'timeout' => 10,
2810 + 'timeout' => 3, // Short timeout
2843 2811 'sslverify' => false,
2844 - 'redirection' => 1,
2845 - 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
2812 + 'redirection' => 1
2846 2813 ));
2847 2814
2848 2815 if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
2849 2816 // Found a sitemap index - parse it to get sub-sitemaps
@@ -2901,15 +2868,10 @@
2901 2868 private function parse_sitemap_index($url) {
2902 2869 $sub_sitemaps = array();
2903 2870
2904 2871 $response = wp_remote_get($url, array(
2905 - 'timeout' => 30,
2906 - 'sslverify' => false,
2907 - 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
2908 - 'headers' => array(
2909 - 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
2910 - 'Accept-Language' => 'en-US,en;q=0.9',
2911 - ),
2872 + 'timeout' => 5,
2873 + 'sslverify' => false
2912 2874 ));
2913 2875
2914 2876 if (is_wp_error($response)) {
2915 2877 return $sub_sitemaps;
@@ -2960,15 +2922,10 @@
2960 2922 * Get URL count from a sitemap
2961 2923 */
2962 2924 private function get_sitemap_url_count($url) {
2963 2925 $response = wp_remote_get($url, array(
2964 - 'timeout' => 30,
2965 - 'sslverify' => false,
2966 - 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
2967 - 'headers' => array(
2968 - 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
2969 - 'Accept-Language' => 'en-US,en;q=0.9',
2970 - ),
2926 + 'timeout' => 10,
2927 + 'sslverify' => false
2971 2928 ));
2972 2929
2973 2930 if (is_wp_error($response)) {
2974 2931 return 0;
@@ -2991,11 +2948,10 @@
2991 2948 $sitemaps = array();
2992 2949 $robots_url = trailingslashit($site_url) . 'robots.txt';
2993 2950
2994 2951 $response = wp_remote_get($robots_url, array(
2995 - 'timeout' => 15,
2996 - 'sslverify' => false,
2997 - 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
2952 + 'timeout' => 5,
2953 + 'sslverify' => false
2998 2954 ));
2999 2955
3000 2956 if (is_wp_error($response)) {
3001 2957 return $sitemaps;
@@ -7121,37 +7077,11 @@
7121 7077 throw new Exception('Unknown item type: ' . $item_type);
7122 7078 }
7123 7079
7124 7080 if (is_wp_error($result)) {
7125 - $error_code = $result->get_error_code();
7126 - // Content errors (empty page, sanitization) are permanent — retrying won't help
7127 - $permanent_codes = array('empty_page', 'empty_after_sanitization', 'no_api_key', 'page_not_found');
7128 - if (in_array($error_code, $permanent_codes)) {
7129 - // Mark as permanently failed — set attempts = max_attempts so it won't be retried
7130 - $current_item = $wpdb->get_row($wpdb->prepare(
7131 - "SELECT max_attempts FROM $table_name WHERE id = %d", $item_id
7132 - ));
7133 - $wpdb->update(
7134 - $table_name,
7135 - array(
7136 - 'status' => 'failed',
7137 - 'error_message' => $result->get_error_message(),
7138 - 'attempts' => $current_item ? $current_item->max_attempts : 3
7139 - ),
7140 - array('id' => $item_id),
7141 - array('%s', '%s', '%d'),
7142 - array('%d')
7143 - );
7144 - wp_send_json_error(array(
7145 - 'message' => $result->get_error_message(),
7146 - 'permanent_failure' => true,
7147 - 'item_id' => $item_id
7148 - ));
7149 - return;
7150 - }
7151 7081 throw new Exception($result->get_error_message());
7152 7082 }
7153 -
7083 +
7154 7084 if ($result === false) {
7155 7085 throw new Exception('Processing returned false - item may be empty or invalid');
7156 7086 }
7157 7087
@@ -7441,24 +7371,21 @@
7441 7371 return new WP_Error('no_pages', 'PDF has no pages');
7442 7372 }
7443 7373
7444 7374 $processed = 0;
7445 - $skipped_pages = array();
7446 7375
7447 7376 for ($i = 0; $i < $total_pages; $i++) {
7448 - $page_num = $i + 1;
7449 7377 $text = $pages[$i]->getText();
7450 7378 if (empty($text)) {
7451 - $skipped_pages[] = 'Page ' . $page_num . ': No text could be extracted — page may contain only images, links, or non-standard encoding';
7452 7379 continue;
7453 7380 }
7454 7381
7455 7382 $sanitized = $this->mxchat_sanitize_content_for_api($text);
7456 7383 if (empty($sanitized)) {
7457 - $skipped_pages[] = 'Page ' . $page_num . ': Text was extracted but contained only special characters, control codes, or unsupported content';
7458 7384 continue;
7459 7385 }
7460 7386
7387 + $page_num = $i + 1;
7461 7388 $metadata = array(
7462 7389 'document_type' => 'pdf',
7463 7390 'total_pages' => $total_pages,
7464 7391 'current_page' => $page_num,
@@ -7482,12 +7409,8 @@
7482 7409
7483 7410 // Clean up the temp PDF file
7484 7411 wp_delete_file($pdf_path);
7485 7412
7486 - if (!empty($skipped_pages)) {
7487 - error_log('MxChat PDF: Skipped ' . count($skipped_pages) . ' of ' . $total_pages . ' pages: ' . implode('; ', $skipped_pages));
7488 - }
7489 -
7490 7413 return $processed > 0 ? true : false;
7491 7414
7492 7415 } catch (Exception $e) {
7493 7416 if (file_exists($pdf_path)) {
@@ -7652,17 +7575,18 @@
7652 7575 return new WP_Error('page_not_found', 'Page ' . $page_number . ' not found in PDF');
7653 7576 }
7654 7577
7655 7578 $text = $pages[$page_number - 1]->getText();
7656 -
7579 +
7657 7580 if (empty($text)) {
7658 - return new WP_Error('empty_page', 'Page ' . $page_number . ': No text could be extracted — page may contain only images, links, or non-standard encoding');
7581 + // Not an error - just an empty page
7582 + return false;
7659 7583 }
7660 -
7584 +
7661 7585 $sanitized = $this->mxchat_sanitize_content_for_api($text);
7662 -
7586 +
7663 7587 if (empty($sanitized)) {
7664 - return new WP_Error('empty_after_sanitization', 'Page ' . $page_number . ': Text was extracted but contained only special characters, control codes, or unsupported content that was removed during cleanup');
7588 + return false;
7665 7589 }
7666 7590
7667 7591 // Create metadata
7668 7592 $metadata = array(
@@ -7766,16 +7690,17 @@
7766 7690
7767 7691 // Calculate percentage
7768 7692 $percentage = $total > 0 ? round((($completed + $failed) / $total) * 100) : 0;
7769 7693
7770 - // Get failed items details (include all failed items, not just those that exhausted retries)
7694 + // Get failed items details
7771 7695 $failed_items = array();
7772 7696 if ($failed > 0) {
7773 7697 $failed_items = $wpdb->get_results($wpdb->prepare(
7774 - "SELECT item_type, item_data, error_message, attempts
7775 - FROM $table_name
7776 - WHERE queue_id = %s
7698 + "SELECT item_type, item_data, error_message, attempts
7699 + FROM $table_name
7700 + WHERE queue_id = %s
7777 7701 AND status = 'failed'
7702 + AND attempts >= max_attempts
7778 7703 ORDER BY id DESC
7779 7704 LIMIT 50",
7780 7705 $queue_id
7781 7706 ));