1+ <?php
2+ /**
3+ * Plugin Name: CBX AI Bot Tracker
4+ * Description: Tracks AI bot visits (OpenAI, Anthropic, Perplexity, etc.) and shows visit logs in admin dashboard.
5+ * Version: 1.0.0
6+ * Author: Codeboxr
7+ * Text Domain: cbxaibottracker
8+ * Domain Path: /languages
9+ */
10+
11+ if ( ! defined ( 'ABSPATH ' ) ) exit ;
12+
13+ class CBXAIBotTracker {
14+
15+ private $ table_name ;
16+
17+ private $ known_ai_bots = [
18+ 'GPTBot ' => 'OpenAI ' ,
19+ 'ChatGPT-User ' => 'OpenAI ' ,
20+ 'ClaudeBot ' => 'Anthropic ' ,
21+ 'Claude-Web ' => 'Anthropic ' ,
22+ 'PerplexityBot ' => 'Perplexity ' ,
23+ 'Google-Extended ' => 'Google AI ' ,
24+ 'CCBot ' => 'Common Crawl ' ,
25+ 'Amazonbot ' => 'Amazon AI '
26+ ];
27+
28+ public function __construct () {
29+ global $ wpdb ;
30+ $ this ->table_name = $ wpdb ->prefix . 'cbxaibottracker_visits ' ;
31+
32+ register_activation_hook (__FILE__ , [$ this , 'create_table ' ]);
33+
34+ add_action ('plugins_loaded ' , [$ this , 'load_textdomain ' ]);
35+ add_action ('init ' , [$ this , 'track_bot_visit ' ]);
36+ add_action ('admin_menu ' , [$ this , 'register_admin_menu ' ]);
37+ add_action ('admin_init ' , [$ this , 'handle_delete ' ]);
38+ }
39+
40+ /* ----------------------------
41+ * Load Translation
42+ * ---------------------------- */
43+ public function load_textdomain () {
44+ load_plugin_textdomain (
45+ 'cbxaibottracker ' ,
46+ false ,
47+ dirname (plugin_basename (__FILE__ )) . '/languages '
48+ );
49+ }
50+
51+ /* ----------------------------
52+ * Create DB Table
53+ * ---------------------------- */
54+ public function create_table () {
55+ global $ wpdb ;
56+ $ charset_collate = $ wpdb ->get_charset_collate ();
57+
58+ $ sql = "CREATE TABLE {$ this ->table_name } (
59+ id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
60+ bot_name VARCHAR(150) NOT NULL,
61+ bot_source VARCHAR(150) NOT NULL,
62+ request_url TEXT NOT NULL,
63+ visit_count BIGINT(20) UNSIGNED DEFAULT 1,
64+ last_visit DATETIME DEFAULT CURRENT_TIMESTAMP,
65+ PRIMARY KEY (id),
66+ UNIQUE KEY bot_url (bot_name, request_url(191))
67+ ) $ charset_collate; " ;
68+
69+ require_once ABSPATH . 'wp-admin/includes/upgrade.php ' ;
70+ dbDelta ($ sql );
71+ }
72+
73+ /* ----------------------------
74+ * Track AI Bot Visit
75+ * ---------------------------- */
76+ public function track_bot_visit () {
77+
78+ if ( is_admin () ) return ;
79+
80+ if ( empty ($ _SERVER ['HTTP_USER_AGENT ' ]) ) return ;
81+
82+ $ user_agent = $ _SERVER ['HTTP_USER_AGENT ' ];
83+ $ bot_detected = false ;
84+ $ bot_name = '' ;
85+ $ bot_source = '' ;
86+
87+ foreach ( $ this ->known_ai_bots as $ signature => $ source ) {
88+ if ( stripos ($ user_agent , $ signature ) !== false ) {
89+ $ bot_detected = true ;
90+ $ bot_name = $ signature ;
91+ $ bot_source = $ source ;
92+ break ;
93+ }
94+ }
95+
96+ if ( ! $ bot_detected ) return ;
97+
98+ $ request_url = esc_url_raw (home_url ($ _SERVER ['REQUEST_URI ' ]));
99+
100+ global $ wpdb ;
101+
102+ $ existing = $ wpdb ->get_row (
103+ $ wpdb ->prepare (
104+ "SELECT * FROM {$ this ->table_name } WHERE bot_name = %s AND request_url = %s " ,
105+ $ bot_name ,
106+ $ request_url
107+ )
108+ );
109+
110+ if ( $ existing ) {
111+ $ wpdb ->update (
112+ $ this ->table_name ,
113+ [
114+ 'visit_count ' => $ existing ->visit_count + 1 ,
115+ 'last_visit ' => current_time ('mysql ' )
116+ ],
117+ ['id ' => $ existing ->id ]
118+ );
119+ } else {
120+ $ wpdb ->insert (
121+ $ this ->table_name ,
122+ [
123+ 'bot_name ' => $ bot_name ,
124+ 'bot_source ' => $ bot_source ,
125+ 'request_url ' => $ request_url ,
126+ 'visit_count ' => 1 ,
127+ 'last_visit ' => current_time ('mysql ' )
128+ ]
129+ );
130+ }
131+ }
132+
133+ /* ----------------------------
134+ * Admin Menu
135+ * ---------------------------- */
136+ public function register_admin_menu () {
137+ add_menu_page (
138+ __ ('AI Bot Tracker ' , 'cbxaibottracker ' ),
139+ __ ('AI Bot Tracker ' , 'cbxaibottracker ' ),
140+ 'manage_options ' ,
141+ 'cbxaibottracker ' ,
142+ [$ this , 'admin_page ' ],
143+ 'dashicons-visibility ' ,
144+ 27
145+ );
146+ }
147+
148+ /* ----------------------------
149+ * Handle Delete
150+ * ---------------------------- */
151+ public function handle_delete () {
152+
153+ if ( ! isset ($ _GET ['cbx_delete ' ]) ) return ;
154+ if ( ! current_user_can ('manage_options ' ) ) return ;
155+
156+ $ id = intval ($ _GET ['cbx_delete ' ]);
157+ check_admin_referer ('cbx_delete_bot_ ' . $ id );
158+
159+ global $ wpdb ;
160+ $ wpdb ->delete ($ this ->table_name , ['id ' => $ id ]);
161+
162+ wp_redirect (admin_url ('admin.php?page=cbxaibottracker&deleted=1 ' ));
163+ exit ;
164+ }
165+
166+ /* ----------------------------
167+ * Admin Page
168+ * ---------------------------- */
169+ public function admin_page () {
170+
171+ global $ wpdb ;
172+
173+ $ results = $ wpdb ->get_results (
174+ "SELECT * FROM {$ this ->table_name } ORDER BY last_visit DESC LIMIT 200 "
175+ );
176+
177+ echo '<div class="wrap"> ' ;
178+ echo '<h1> ' . esc_html__ ('AI Bot Visits ' , 'cbxaibottracker ' ) . '</h1> ' ;
179+
180+ if ( isset ($ _GET ['deleted ' ]) ) {
181+ echo '<div class="notice notice-success is-dismissible"> ' ;
182+ echo '<p> ' . esc_html__ ('Entry deleted successfully. ' , 'cbxaibottracker ' ) . '</p> ' ;
183+ echo '</div> ' ;
184+ }
185+
186+ echo '<table class="widefat striped"> ' ;
187+ echo '<thead><tr> ' ;
188+ echo '<th> ' . esc_html__ ('Bot Name ' , 'cbxaibottracker ' ) . '</th> ' ;
189+ echo '<th> ' . esc_html__ ('Source ' , 'cbxaibottracker ' ) . '</th> ' ;
190+ echo '<th> ' . esc_html__ ('URL ' , 'cbxaibottracker ' ) . '</th> ' ;
191+ echo '<th> ' . esc_html__ ('Visits ' , 'cbxaibottracker ' ) . '</th> ' ;
192+ echo '<th> ' . esc_html__ ('Last Visit ' , 'cbxaibottracker ' ) . '</th> ' ;
193+ echo '<th> ' . esc_html__ ('Action ' , 'cbxaibottracker ' ) . '</th> ' ;
194+ echo '</tr></thead> ' ;
195+ echo '<tbody> ' ;
196+
197+ if ( $ results ) {
198+ foreach ( $ results as $ row ) {
199+
200+ $ delete_url = wp_nonce_url (
201+ admin_url ('admin.php?page=cbxaibottracker&cbx_delete= ' . $ row ->id ),
202+ 'cbx_delete_bot_ ' . $ row ->id
203+ );
204+
205+ echo '<tr> ' ;
206+ echo '<td> ' . esc_html ($ row ->bot_name ) . '</td> ' ;
207+ echo '<td> ' . esc_html ($ row ->bot_source ) . '</td> ' ;
208+ echo '<td><a href=" ' . esc_url ($ row ->request_url ) . '" target="_blank"> ' . esc_html ($ row ->request_url ) . '</a></td> ' ;
209+ echo '<td> ' . esc_html ($ row ->visit_count ) . '</td> ' ;
210+ echo '<td> ' . esc_html ($ row ->last_visit ) . '</td> ' ;
211+ echo '<td><a href=" ' . esc_url ($ delete_url ) . '" class="button button-small"> ' . esc_html__ ('Delete ' , 'cbxaibottracker ' ) . '</a></td> ' ;
212+ echo '</tr> ' ;
213+ }
214+ } else {
215+ echo '<tr><td colspan="6"> ' . esc_html__ ('No AI bot visits recorded yet. ' , 'cbxaibottracker ' ) . '</td></tr> ' ;
216+ }
217+
218+ echo '</tbody></table> ' ;
219+ echo '</div> ' ;
220+ }
221+ }
222+
223+ new CBXAIBotTracker ();
0 commit comments