Changeset 3488558
- Timestamp:
- 03/23/2026 05:34:01 AM (9 days ago)
- Location:
- ai-discovery-files
- Files:
-
- 56 added
- 1 deleted
- 10 edited
-
tags/1.3.1 (added)
-
tags/1.3.1/LICENSE (added)
-
tags/1.3.1/admin (added)
-
tags/1.3.1/admin/class-admin.php (added)
-
tags/1.3.1/admin/class-settings.php (added)
-
tags/1.3.1/admin/css (added)
-
tags/1.3.1/admin/css/admin.css (added)
-
tags/1.3.1/admin/css/crawlers.css (added)
-
tags/1.3.1/admin/js (added)
-
tags/1.3.1/admin/js/admin.js (added)
-
tags/1.3.1/admin/js/crawlers.js (added)
-
tags/1.3.1/admin/views (added)
-
tags/1.3.1/admin/views/partials (added)
-
tags/1.3.1/admin/views/partials/crawler-bot-detail.php (added)
-
tags/1.3.1/admin/views/partials/crawler-bot-selector.php (added)
-
tags/1.3.1/admin/views/partials/crawler-file-access-widget.php (added)
-
tags/1.3.1/admin/views/partials/crawler-log-viewer.php (added)
-
tags/1.3.1/admin/views/partials/directory-cta.php (added)
-
tags/1.3.1/admin/views/partials/review-banner.php (added)
-
tags/1.3.1/admin/views/partials/tier-progress.php (added)
-
tags/1.3.1/admin/views/partials/verify-modal.php (added)
-
tags/1.3.1/admin/views/settings-page.php (added)
-
tags/1.3.1/admin/views/tab-advanced.php (added)
-
tags/1.3.1/admin/views/tab-content.php (added)
-
tags/1.3.1/admin/views/tab-crawlers.php (added)
-
tags/1.3.1/admin/views/tab-identity.php (added)
-
tags/1.3.1/admin/views/tab-permissions.php (added)
-
tags/1.3.1/admin/views/tab-preview.php (added)
-
tags/1.3.1/admin/views/tab-status.php (added)
-
tags/1.3.1/ai-discovery-files.php (added)
-
tags/1.3.1/includes (added)
-
tags/1.3.1/includes/class-crawler-analytics.php (added)
-
tags/1.3.1/includes/class-crawler-conflicts.php (added)
-
tags/1.3.1/includes/class-crawler-logger.php (added)
-
tags/1.3.1/includes/class-crawler-registry.php (added)
-
tags/1.3.1/includes/class-data-collector.php (added)
-
tags/1.3.1/includes/class-generator.php (added)
-
tags/1.3.1/includes/class-plugin.php (added)
-
tags/1.3.1/includes/class-server.php (added)
-
tags/1.3.1/includes/class-validator.php (added)
-
tags/1.3.1/languages (added)
-
tags/1.3.1/languages/ai-discovery-files.pot (added)
-
tags/1.3.1/languages/index.php (added)
-
tags/1.3.1/readme.txt (added)
-
tags/1.3.1/templates (added)
-
tags/1.3.1/templates/ai-json.php (added)
-
tags/1.3.1/templates/ai-txt.php (added)
-
tags/1.3.1/templates/brand-txt.php (added)
-
tags/1.3.1/templates/developer-ai-txt.php (added)
-
tags/1.3.1/templates/faq-ai-txt.php (added)
-
tags/1.3.1/templates/identity-json.php (added)
-
tags/1.3.1/templates/llm-txt.php (added)
-
tags/1.3.1/templates/llms-html.php (added)
-
tags/1.3.1/templates/llms-txt.php (added)
-
tags/1.3.1/templates/robots-ai-txt.php (added)
-
tags/1.3.1/uninstall.php (added)
-
trunk/admin/class-admin.php (modified) (4 diffs)
-
trunk/admin/js/crawlers.js (modified) (12 diffs)
-
trunk/admin/views/partials/crawler-bot-detail.php (modified) (2 diffs)
-
trunk/admin/views/partials/crawler-dashboard-widget.php (deleted)
-
trunk/admin/views/partials/crawler-log-viewer.php (modified) (1 diff)
-
trunk/admin/views/tab-crawlers.php (modified) (8 diffs)
-
trunk/ai-discovery-files.php (modified) (2 diffs)
-
trunk/includes/class-crawler-logger.php (modified) (5 diffs)
-
trunk/includes/class-plugin.php (modified) (1 diff)
-
trunk/includes/class-server.php (modified) (1 diff)
-
trunk/readme.txt (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ai-discovery-files/trunk/admin/class-admin.php
r3488445 r3488558 700 700 701 701 wp_add_dashboard_widget( 702 'aidf_crawler_activity',703 __( 'AI Crawler Activity', 'ai-discovery-files' ),704 array( __CLASS__, 'render_crawler_activity_widget' )705 );706 707 wp_add_dashboard_widget(708 702 'aidf_file_access', 709 703 __( 'AI Discovery File Access', 'ai-discovery-files' ), … … 713 707 714 708 /** 715 * Render the Crawler Activity dashboard widget.716 *717 * @since 1.3.0718 */719 public static function render_crawler_activity_widget() {720 include AIDF_PLUGIN_DIR . 'admin/views/partials/crawler-dashboard-widget.php';721 }722 723 /**724 709 * Render the File Access dashboard widget. 725 710 * … … 746 731 <style> 747 732 /* AI Discovery Files — Dashboard Widget Styles */ 748 .aidf-widget-top-bots { color: #5a5d6b; font-size: 13px; }749 .aidf-widget-warning { color: #d97706; font-size: 13px; }750 .aidf-widget-warning .dashicons { font-size: 16px; width: 16px; height: 16px; vertical-align: text-bottom; }751 733 .aidf-widget-empty { color: #8b8fa3; font-style: italic; } 752 734 .aidf-widget-link { text-align: right; margin-bottom: 0; } … … 796 778 * 797 779 * Returns detailed analytics for a single bot: daily trend, status code 798 * breakdown, pages visited, discovery file access, and first/last seen.780 * breakdown, files accessed, discovery file access, and first/last seen. 799 781 * 800 782 * @since 1.3.0 -
ai-discovery-files/trunk/admin/js/crawlers.js
r3488445 r3488558 4 4 * Dashboard: period toggle, AJAX loading, chart rendering, summary cards, 5 5 * breakdown table, file access panel, insights alerts. 6 * Only discovery-file-level access is logged (not general page visits). 6 7 * Settings: bot selector presets, save settings, clear log. 7 8 * … … 179 180 $dashboard.addClass('aidf-dashboard--loading'); 180 181 181 // Load overview + file access in parallel.182 // Load overview + file access data in parallel. 182 183 var overviewReq = $.ajax({ 183 184 url: aidf.ajaxUrl, … … 435 436 statusHtml = '<span class="aidf-status-badge aidf-status-badge--green">Active</span>'; 436 437 } else { 437 statusHtml = '<span class="aidf-status-badge aidf-status-badge--gray">No visits</span>';438 statusHtml = '<span class="aidf-status-badge aidf-status-badge--gray">No activity</span>'; 438 439 } 439 440 … … 445 446 '<td>' + formatNumber(row.visit_count) + '</td>' + 446 447 '<td title="' + escHtml(row.last_seen || '') + '">' + timeAgo(row.last_seen) + '</td>' + 447 '<td><code class="aidf-mono-sm">' + escHtml(row.top_ page || '\u2014') + '</code></td>' +448 '<td><code class="aidf-mono-sm">' + escHtml(row.top_file || row.top_page || '\u2014') + '</code></td>' + 448 449 '<td>' + statusHtml + '</td>' + 449 450 '</tr>'; … … 902 903 903 904 /** 904 * Render a single-colour bar chart for daily visittrend.905 * Render a single-colour bar chart for daily access trend. 905 906 * Bars are proportional to the day with the highest count. 906 907 */ … … 910 911 var dates = Object.keys(dailyTrend).sort(); 911 912 if (!dates.length) { 912 $container.html('<p class="aidf-text-muted">No visitdata in this period.</p>');913 $container.html('<p class="aidf-text-muted">No access data in this period.</p>'); 913 914 return; 914 915 } … … 936 937 var label = parts.length === 3 ? parseInt(parts[1], 10) + '/' + parseInt(parts[2], 10) : date; 937 938 938 html += '<div class="aidf-detail-trend__bar" title="' + escHtml(date) + ': ' + formatNumber(count) + ' visits">' +939 html += '<div class="aidf-detail-trend__bar" title="' + escHtml(date) + ': ' + formatNumber(count) + ' accesses">' + 939 940 '<div class="aidf-detail-trend__fill" style="height:' + pct + '%;"></div>' + 940 941 '<span class="aidf-detail-trend__label">' + escHtml(label) + '</span>' + … … 1007 1008 1008 1009 /** 1009 * Render the pages visited table, sorted by visitcount descending.1010 * Limited to top 50 pages to keep the view manageable.1010 * Render the files accessed table, sorted by access count descending. 1011 * Limited to top 50 files to keep the view manageable. 1011 1012 */ 1012 1013 function renderDetailPages(pages) { … … 1015 1016 var paths = Object.keys(pages); 1016 1017 if (!paths.length) { 1017 $container.html('<p class="aidf-text-muted">No page visitdata available.</p>');1018 $container.html('<p class="aidf-text-muted">No file access data available.</p>'); 1018 1019 return; 1019 1020 } … … 1029 1030 var html = '<table class="aidf-detail-table">' + 1030 1031 '<thead><tr>' + 1031 '<th> URL Path</th>' +1032 '<th> Visits</th>' +1032 '<th>File</th>' + 1033 '<th>Accesses</th>' + 1033 1034 '<th>Last Access</th>' + 1034 1035 '</tr></thead>' + … … 1048 1049 1049 1050 if (paths.length > 50) { 1050 html += '<p class="aidf-detail-table__note">Showing top 50 of ' + formatNumber(paths.length) + ' pages. Export the log for the full list.</p>';1051 html += '<p class="aidf-detail-table__note">Showing top 50 of ' + formatNumber(paths.length) + ' files. Export the log for the full list.</p>'; 1051 1052 } 1052 1053 … … 1272 1273 '<th>Time</th>' + 1273 1274 '<th>Bot</th>' + 1274 '<th> URL Path</th>' +1275 '<th>File</th>' + 1275 1276 '<th>Status</th>' + 1276 1277 '</tr></thead>' + -
ai-discovery-files/trunk/admin/views/partials/crawler-bot-detail.php
r3488445 r3488558 35 35 </div> 36 36 37 <!-- Visittrend chart -->37 <!-- Access trend chart --> 38 38 <div class="aidf-panel aidf-bot-detail__panel"> 39 39 <div class="aidf-panel-header"> 40 <h4 class="aidf-panel-title"><?php esc_html_e( ' VisitTrend', 'ai-discovery-files' ); ?></h4>40 <h4 class="aidf-panel-title"><?php esc_html_e( 'Access Trend', 'ai-discovery-files' ); ?></h4> 41 41 </div> 42 42 <div class="aidf-panel-body" id="aidf-bot-detail-chart"></div> … … 51 51 </div> 52 52 53 <!-- Pages visited -->53 <!-- Files accessed --> 54 54 <div class="aidf-panel aidf-bot-detail__panel"> 55 55 <div class="aidf-panel-header"> 56 <h4 class="aidf-panel-title"><?php esc_html_e( ' Pages Visited', 'ai-discovery-files' ); ?></h4>56 <h4 class="aidf-panel-title"><?php esc_html_e( 'Files Accessed', 'ai-discovery-files' ); ?></h4> 57 57 </div> 58 58 <div class="aidf-panel-body" id="aidf-bot-detail-pages"></div> -
ai-discovery-files/trunk/admin/views/partials/crawler-log-viewer.php
r3488445 r3488558 51 51 id="aidf-log-filter-path" 52 52 class="aidf-log-filter__path" 53 placeholder="<?php esc_attr_e( 'Filter by URL path...', 'ai-discovery-files' ); ?>"53 placeholder="<?php esc_attr_e( 'Filter by file...', 'ai-discovery-files' ); ?>" 54 54 > 55 55 <button type="button" class="aidf-btn aidf-btn--sm aidf-btn--primary" id="aidf-apply-log-filters"> -
ai-discovery-files/trunk/admin/views/tab-crawlers.php
r3488445 r3488558 73 73 <span class="dashicons dashicons-chart-bar"></span> 74 74 <h3 class="aidf-empty-state__heading"><?php esc_html_e( 'AI Crawler Analytics', 'ai-discovery-files' ); ?></h3> 75 <p class="aidf-empty-state__description"><?php esc_html_e( 'See which AI bots visit your site, which pages they access,and whether any are being blocked.', 'ai-discovery-files' ); ?></p>75 <p class="aidf-empty-state__description"><?php esc_html_e( 'See which AI bots access your discovery files and whether any are being blocked.', 'ai-discovery-files' ); ?></p> 76 76 <button type="button" class="aidf-btn aidf-btn--primary" id="aidf-enable-logging-cta"><?php esc_html_e( 'Enable Logging', 'ai-discovery-files' ); ?></button> 77 77 </div> … … 93 93 <div class="aidf-stat-card aidf-stat-card--muted"> 94 94 <div class="aidf-stat-card__value">0</div> 95 <div class="aidf-stat-card__label"><?php esc_html_e( ' Total Visits', 'ai-discovery-files' ); ?></div>95 <div class="aidf-stat-card__label"><?php esc_html_e( 'File Accesses', 'ai-discovery-files' ); ?></div> 96 96 </div> 97 97 <div class="aidf-stat-card aidf-stat-card--muted"> … … 112 112 <div class="aidf-notice aidf-notice--info" style="margin: 0;"> 113 113 <span class="dashicons dashicons-info"></span> 114 <span><?php esc_html_e( 'Logging is active. Data will appear as AI crawlers visit your site— this typically takes 24-48 hours.', 'ai-discovery-files' ); ?></span>114 <span><?php esc_html_e( 'Logging is active. Data will appear as AI crawlers access your discovery files — this typically takes 24-48 hours.', 'ai-discovery-files' ); ?></span> 115 115 </div> 116 116 … … 131 131 <div class="aidf-stat-card"> 132 132 <div class="aidf-stat-card__value" data-card="total_visits"><?php echo esc_html( number_format_i18n( $aidf_summary['total_visits'] ) ); ?></div> 133 <div class="aidf-stat-card__label"><?php esc_html_e( ' Total Visits', 'ai-discovery-files' ); ?></div>133 <div class="aidf-stat-card__label"><?php esc_html_e( 'File Accesses', 'ai-discovery-files' ); ?></div> 134 134 </div> 135 135 <div class="aidf-stat-card"> … … 160 160 <table class="aidf-sr-only" id="aidf-chart-data-table"> 161 161 <caption><?php esc_html_e( 'AI crawler activity over the selected period', 'ai-discovery-files' ); ?></caption> 162 <thead><tr><th><?php esc_html_e( 'Date', 'ai-discovery-files' ); ?></th><th><?php esc_html_e( 'Bot', 'ai-discovery-files' ); ?></th><th><?php esc_html_e( ' Visits', 'ai-discovery-files' ); ?></th></tr></thead>162 <thead><tr><th><?php esc_html_e( 'Date', 'ai-discovery-files' ); ?></th><th><?php esc_html_e( 'Bot', 'ai-discovery-files' ); ?></th><th><?php esc_html_e( 'Accesses', 'ai-discovery-files' ); ?></th></tr></thead> 163 163 <tbody id="aidf-chart-data-tbody"></tbody> 164 164 </table> … … 175 175 <th><?php esc_html_e( 'Bot', 'ai-discovery-files' ); ?></th> 176 176 <th><?php esc_html_e( 'Category', 'ai-discovery-files' ); ?></th> 177 <th><?php esc_html_e( ' Visits', 'ai-discovery-files' ); ?></th>177 <th><?php esc_html_e( 'Accesses', 'ai-discovery-files' ); ?></th> 178 178 <th><?php esc_html_e( 'Last Seen', 'ai-discovery-files' ); ?></th> 179 <th><?php esc_html_e( 'Top Page', 'ai-discovery-files' ); ?></th>179 <th><?php esc_html_e( 'Top File', 'ai-discovery-files' ); ?></th> 180 180 <th><?php esc_html_e( 'Status', 'ai-discovery-files' ); ?></th> 181 181 </tr> … … 207 207 <span class="aidf-status-badge aidf-status-badge--green"><?php esc_html_e( 'Active', 'ai-discovery-files' ); ?></span> 208 208 <?php else : ?> 209 <span class="aidf-status-badge aidf-status-badge--gray"><?php esc_html_e( 'No visits', 'ai-discovery-files' ); ?></span>209 <span class="aidf-status-badge aidf-status-badge--gray"><?php esc_html_e( 'No activity', 'ai-discovery-files' ); ?></span> 210 210 <?php endif; ?> 211 211 </td> … … 316 316 <div class="aidf-setting-row__label"> 317 317 <label for="aidf-crawler-logging"><?php esc_html_e( 'Enable Crawler Logging', 'ai-discovery-files' ); ?></label> 318 <p class="aidf-setting-row__help"><?php esc_html_e( 'When enabled, the plugin detects and logs AI crawler visits to your site. Disabled by default — no performance impact when off.', 'ai-discovery-files' ); ?></p>318 <p class="aidf-setting-row__help"><?php esc_html_e( 'When enabled, the plugin detects and logs AI crawler access to your discovery files. Disabled by default — no performance impact when off.', 'ai-discovery-files' ); ?></p> 319 319 </div> 320 320 <div class="aidf-setting-row__control"> -
ai-discovery-files/trunk/ai-discovery-files.php
r3488445 r3488558 4 4 * Plugin URI: https://www.ai-visibility.org.uk/wordpress-plugin/ai-discovery-files/ 5 5 * Description: The only WordPress plugin that generates all 10 AI Discovery Files and tracks which AI bots visit your site. Built by 365i. 6 * Version: 1.3. 06 * Version: 1.3.1 7 7 * Requires at least: 6.2 8 8 * Requires PHP: 8.0 … … 24 24 * Plugin constants. 25 25 */ 26 define( 'AIDF_VERSION', '1.3. 0' );26 define( 'AIDF_VERSION', '1.3.1' ); 27 27 define( 'AIDF_PLUGIN_FILE', __FILE__ ); 28 28 define( 'AIDF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); -
ai-discovery-files/trunk/includes/class-crawler-logger.php
r3488445 r3488558 1 1 <?php 2 2 /** 3 * Crawler logger — captures AI bot hits and flushes them to the database.3 * Crawler logger — logs AI bot access to discovery files served by AIDF_Server. 4 4 * 5 * Hooks into `init` at priority 1 to detect bots by user agent, buffers 6 * any matched hit in memory, then writes to the database on `shutdown` 7 * so the final HTTP status code is available at flush time. 5 * Called directly from AIDF_Server::serve_file() after a file is generated 6 * successfully. This approach guarantees every request reaches PHP even on 7 * sites with page caching or CDN edge caching, because discovery file 8 * responses use s-maxage=0. 8 9 * 9 10 * @package AIDF … … 16 17 17 18 /** 18 * Handles AI crawler detection , hit buffering, and log persistence.19 * Handles AI crawler detection and log persistence for discovery file requests. 19 20 * 20 * All public methods are static so the class can be called without an 21 * instance — it is effectively a service with class-level state held 22 * in the private static $buffer array. 21 * All public methods are static so the class can be called without an instance. 23 22 * 24 23 * @since 1.3.0 … … 27 26 28 27 /** 29 * In-memory buffer of detected bot hits for the currentrequest.28 * Detect and log an AI crawler hit for a discovery file request. 30 29 * 31 * Each entry is an associative array with keys: 32 * - bot_name (string) Human-readable bot name. 33 * - bot_ua (string) Raw user-agent string, truncated to 500 chars. 34 * - url_path (string) Requested URL path, truncated to 500 chars. 30 * Called from AIDF_Server::serve_file() after the file content has been 31 * generated successfully. Checks the user agent against the enabled bot 32 * registry and writes a single row to the crawler log table when matched. 33 * 34 * Reads the raw aidf_settings option directly instead of 35 * AIDF_Plugin::get_settings() to avoid triggering page URL lookups 36 * that require $wp_rewrite — which may not be fully initialised at 37 * serve_file() time. 35 38 * 36 39 * @since 1.3.0 37 * @var array<int, array<string, string>>38 */39 private static $buffer = array();40 41 /**42 * Initialise the logger for the current request.43 40 * 44 * Reads the plugin settings and, when logging is enabled, registers 45 * the `detect_bot` and `flush_buffer` hooks. Admin, AJAX, and cron 46 * requests are skipped immediately; REST API requests are skipped 47 * inside `detect_bot` because the REST_REQUEST constant is not yet 48 * defined at `plugins_loaded` time. 49 * 50 * @since 1.3.0 41 * @param string $file_slug The file type slug (e.g. 'llms-txt', 'ai-txt'). 51 42 * @return void 52 43 */ 53 public static function init() { 54 // Read the raw option directly instead of AIDF_Plugin::get_settings() 55 // because get_settings() calls get_defaults() which triggers page URL 56 // lookups that need $wp_rewrite — not yet available at plugins_loaded time. 44 public static function maybe_log_hit( $file_slug ) { 45 // Read the raw option to avoid the $wp_rewrite timing issue. 57 46 $saved = get_option( 'aidf_settings', array() ); 58 47 59 48 if ( empty( $saved['crawler_logging_enabled'] ) ) { 60 return;61 }62 63 // Only log front-end requests.64 if ( is_admin() || wp_doing_ajax() || wp_doing_cron() ) {65 return;66 }67 68 add_action( 'init', array( __CLASS__, 'detect_bot' ), 1 );69 add_action( 'shutdown', array( __CLASS__, 'flush_buffer' ) );70 }71 72 /**73 * Match the current request's user agent against enabled bot definitions.74 *75 * Runs on the `init` hook at priority 1. Skips REST API requests.76 * When a match is found the hit is added to the in-memory buffer;77 * the buffer is written to the database later in `flush_buffer`.78 *79 * @since 1.3.080 * @return void81 */82 public static function detect_bot() {83 // Skip REST API requests (constant not available at plugins_loaded time).84 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {85 49 return; 86 50 } … … 94 58 } 95 59 96 $settings = AIDF_Plugin::get_settings();97 $enabled_keys = $settings['crawler_enabled_bots'];60 // Build the enabled-only bot subset from the registry. 61 $enabled_keys = isset( $saved['crawler_enabled_bots'] ) ? (array) $saved['crawler_enabled_bots'] : array(); 98 62 $all_bots = AIDF_Crawler_Registry::get_bots(); 99 63 100 // Build the enabled-only subset from the full bot registry.101 64 $enabled_bots = array(); 102 65 foreach ( $enabled_keys as $key ) { … … 112 75 } 113 76 114 $url_path = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '/'; 115 $url_path = wp_parse_url( $url_path, PHP_URL_PATH ); 116 if ( ! is_string( $url_path ) ) { 117 $url_path = '/'; 118 } 119 120 self::$buffer[] = array( 121 'bot_name' => $all_bots[ $matched_key ]['name'], 122 'bot_ua' => mb_substr( $ua, 0, 500 ), 123 'url_path' => mb_substr( $url_path, 0, 500 ), 124 ); 125 } 126 127 /** 128 * Write buffered hits to the database on shutdown. 129 * 130 * The status code is read here rather than at detection time because 131 * WordPress has not determined the final HTTP response code until the 132 * end of the request lifecycle. After flushing, the buffer is cleared. 133 * 134 * @since 1.3.0 135 * @return void 136 */ 137 public static function flush_buffer() { 138 if ( empty( self::$buffer ) ) { 139 return; 140 } 77 // Build the URL path from the known file type metadata. 78 $file_types = AIDF_Plugin::get_file_types(); 79 $url_path = isset( $file_types[ $file_slug ] ) 80 ? '/' . $file_types[ $file_slug ]['filename'] 81 : '/' . $file_slug; 141 82 142 83 global $wpdb; 143 84 144 85 $table = $wpdb->prefix . 'aidf_crawler_log'; 145 $status_code = http_response_code();86 $status_code = 200; 146 87 $now = current_time( 'mysql', true ); 147 88 148 foreach ( self::$buffer as $hit ) { 149 $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 150 $table, 151 array( 152 'bot_name' => $hit['bot_name'], 153 'bot_ua' => $hit['bot_ua'], 154 'url_path' => $hit['url_path'], 155 'status_code' => $status_code ? $status_code : 200, 156 'created_at' => $now, 157 ), 158 array( '%s', '%s', '%s', '%d', '%s' ) 159 ); 89 $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 90 $table, 91 array( 92 'bot_name' => $all_bots[ $matched_key ]['name'], 93 'bot_ua' => mb_substr( $ua, 0, 500 ), 94 'url_path' => mb_substr( $url_path, 0, 500 ), 95 'status_code' => $status_code, 96 'created_at' => $now, 97 ), 98 array( '%s', '%s', '%s', '%d', '%s' ) 99 ); 160 100 161 /** 162 * Fires after a crawler hit has been written to the log. 163 * 164 * @since 1.3.0 165 * 166 * @param string $bot_name Human-readable bot name. 167 * @param string $url_path Requested URL path. 168 * @param int $status_code HTTP response status code. 169 */ 170 do_action( 'aidf_crawler_hit_logged', $hit['bot_name'], $hit['url_path'], $status_code ); 171 } 172 173 self::$buffer = array(); 101 /** 102 * Fires after a crawler hit has been written to the log. 103 * 104 * @since 1.3.0 105 * 106 * @param string $bot_name Human-readable bot name. 107 * @param string $url_path Requested URL path (e.g. /llms.txt). 108 * @param int $status_code HTTP response status code (always 200). 109 */ 110 do_action( 'aidf_crawler_hit_logged', $all_bots[ $matched_key ]['name'], $url_path, $status_code ); 174 111 } 175 112 -
ai-discovery-files/trunk/includes/class-plugin.php
r3488445 r3488558 39 39 */ 40 40 private function __construct() { 41 AIDF_Crawler_Logger::init();42 41 AIDF_Server::init(); 43 42 -
ai-discovery-files/trunk/includes/class-server.php
r3474794 r3488558 143 143 } 144 144 145 // Log AI crawler access when logging is enabled. 146 if ( class_exists( 'AIDF_Crawler_Logger' ) ) { 147 AIDF_Crawler_Logger::maybe_log_hit( $file_slug ); 148 } 149 145 150 // Send appropriate headers. 146 151 status_header( 200 ); 147 152 header( 'Content-Type: ' . $meta['content_type'] ); 148 header( 'Cache-Control: public, max-age=3600, s-maxage= 43200' );153 header( 'Cache-Control: public, max-age=3600, s-maxage=0' ); 149 154 header( 'X-Robots-Tag: noindex' ); 150 155 header( 'X-AIDF-Generator: AI Discovery Files for WordPress/' . AIDF_VERSION ); -
ai-discovery-files/trunk/readme.txt
r3488445 r3488558 5 5 Tested up to: 6.9 6 6 Requires PHP: 8.0 7 Stable tag: 1.3. 07 Stable tag: 1.3.1 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 The only WordPress plugin that generates all 10 AI Discovery Files and tracks which AI bots visit your site. Built by 365i.11 The only WordPress plugin that generates all 10 AI Discovery Files and shows which AI bots read them. Built by 365i. 12 12 13 13 == Description == … … 66 66 * **Live preview** — see exactly what each file contains before enabling it 67 67 * **Validation** — checks files against the specification and flags issues 68 * **AI Crawler Analytics** — see which AI bots visit your site, how often, and which pages they access68 * **AI Crawler Analytics** — see which AI bots read your AI Discovery Files, with visual dashboards and CSV export 69 69 * **Discovery File Access tracking** — proof that AI bots are reading the files this plugin generates 70 70 * **robots.txt conflict detection** — warns when your robots.txt contradicts your AI visibility settings … … 150 150 = What is AI Crawler Analytics? = 151 151 152 AI Crawler Analytics shows you which AI bots are visiting your site — GPTBot, ClaudeBot, PerplexityBot, Applebot, and 40+ others. You can see how often they visit, which pages they access, and whether any are being blocked by your robots.txt. It also shows which of your AI Discovery Files are being read by AI crawlers, giving you proof that the plugin is working.152 AI Crawler Analytics tracks which AI bots read the AI Discovery Files this plugin generates — llms.txt, ai.txt, identity.json, and the rest. You see exactly which bots accessed which files, how often, and whether any are being blocked by your robots.txt. This gives you direct proof that GPTBot, ClaudeBot, PerplexityBot, and other AI crawlers are consuming your files. The feature works reliably on every hosting platform — including sites with CDN edge caching — because the plugin controls the discovery file responses. Includes bot detail drill-downs, a filterable activity log with CSV export, and two WordPress dashboard widgets. 153 153 154 154 = Will the crawler analytics slow down my website? = 155 155 156 No. When crawler logging is enabled, the plugin checks each request's user agent against a list of known AI bots. For non-bot requests (99.9% of traffic), this check takes less than a millisecond and involves no database queries. Bot hits are buffered in memory and written to the database once per request on shutdown. When logging is disabled (the default), the check is not registeredat all — zero overhead.156 No. The plugin only tracks access to AI Discovery File URLs that it serves (e.g., `/llms.txt`, `/ai.txt`). Normal page loads are completely unaffected — no user agent checks, no database queries, no overhead at all. When a discovery file is served, the plugin logs which bot accessed it. When logging is disabled (the default), no tracking runs at all — zero overhead. 157 157 158 158 = Will this slow down my website? = 159 159 160 No. The plugin only runs when its specific URLs are requested (e.g., `/llms.txt`). It adds zero overhead to your normal page loads. Files are generated on-the-fly from cached settings data. The crawler analytics feature is disabled by default and adds negligible overheadwhen enabled.160 No. The plugin only runs when its specific URLs are requested (e.g., `/llms.txt`). It adds zero overhead to your normal page loads. Files are generated on-the-fly from cached settings data. The crawler analytics feature is disabled by default and only tracks discovery file access when enabled. 161 161 162 162 = What happens if I deactivate the plugin? = … … 179 179 == Changelog == 180 180 181 = 1.3.1 = 182 * Fix: Move bot detection into discovery file server for reliable CDN/cache compatibility 183 * Fix: Set s-maxage=0 on discovery file responses so CDN edge caches pass through to origin 184 * Fix: Remove page-level tracking — all analytics now scoped to discovery file access only 185 * Fix: Consolidate to single dashboard widget (Discovery File Access) 186 * Fix: Update all UI labels and descriptions for file-access-only scope 187 181 188 = 1.3.0 = 182 * New: AI Crawler Analytics — see which AI bots visit your site 183 * New: Dashboard showing bot visits, frequency, and pages accessed 184 * New: Discovery File Access panel — see which bots read your AI Discovery Files 189 * New: AI Crawler Analytics — see which AI bots access your AI Discovery Files 190 * New: Discovery File Access dashboard with bot breakdown and visual access bars 185 191 * New: robots.txt conflict detection with actionable alerts 186 * New: Categorised bot registry with 43 AI crawlers across 8 categories192 * New: Bot detail drill-down with access trends and file breakdown 187 193 * New: Filterable activity log viewer with CSV export 188 * New: Bot detail drill-down with visit trends and page breakdown189 * New: Two WordPress dashboard widgets (Crawler Activity + File Access)194 * New: WordPress dashboard widget (Discovery File Access) 195 * New: 50+ AI crawler definitions (GPTBot, ClaudeBot, PerplexityBot, GrokBot, and more) 190 196 * New: User controls — enable/disable logging, data retention, bot selection 191 197 … … 229 235 == Upgrade Notice == 230 236 237 = 1.3.1 = 238 Fix: Bot detection moved into discovery file server for reliable CDN/cache compatibility. All analytics now scoped to discovery file access. Works on every hosting platform. 239 231 240 = 1.3.0 = 232 New: AI Crawler Analytics. See which AI bots visit your site, track Discovery File access, and detect robots.txt conflicts. Includes dashboard widgets, bot detail drill-downs, filterable log viewer, and CSV export.241 New: AI Crawler Analytics. See which AI bots access your AI Discovery Files, detect robots.txt conflicts, and get proof your files are working. Includes dashboard widget, bot detail drill-downs, filterable log viewer, and CSV export. 233 242 234 243 = 1.1.0 =
Note: See TracChangeset
for help on using the changeset viewer.