PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.2
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.2
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
metasync / views / metasync-otto-bot-statistics.php

metasync-otto-bot-statistics.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.7.2, at views/metasync-otto-bot-statistics.php

360 lines 19.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Bot Statistics Dashboard View
4 *
5 * Displays bot detection statistics, breakdown by type, and unique bot entries
6 * with hit counts.
7 *
8 * @package Metasync
9 * @subpackage Metasync/views
10 * @since 1.0.0
11 */
12
13 // If this file is called directly, abort.
14 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 // Load required classes
19 require_once plugin_dir_path(dirname(__FILE__)) . 'otto/class-metasync-otto-bot-detector.php';
20 require_once plugin_dir_path(dirname(__FILE__)) . 'otto/class-metasync-otto-bot-statistics-database.php';
21
22 // Get database instance
23 $db = Metasync_Otto_Bot_Statistics_Database::get_instance();
24
25 // Get statistics
26 $stats = $db->get_statistics();
27
28 // Get recent requests (unique entries)
29 $recent_requests = $db->get_recent_requests(100);
30
31 // Get whitelabel OTTO name
32 $whitelabel_otto_name = Metasync::get_whitelabel_otto_name();
33
34 // Get current settings
35 $general_options = Metasync::get_option('general') ?? array();
36 $bot_filtering_enabled = isset($general_options['otto_disable_for_bots']) ? (bool)$general_options['otto_disable_for_bots'] : false;
37
38 // Breakdown label config (reused in table)
39 $breakdown_labels = array(
40 'search_engine' => array('label' => 'Search Engines', 'icon' => 'search', 'color' => '#667eea'),
41 'cache_warmer' => array('label' => 'Cache Warmers', 'icon' => 'performance', 'color' => '#667eea'),
42 'perf_auditor' => array('label' => 'Perf Auditors', 'icon' => 'dashboard', 'color' => '#667eea'),
43 'seo_tool' => array('label' => 'SEO Tools', 'icon' => 'chart-line', 'color' => '#667eea'),
44 'social_media' => array('label' => 'Social Media', 'icon' => 'share', 'color' => '#667eea'),
45 'archiver' => array('label' => 'Archivers', 'icon' => 'archive', 'color' => '#667eea'),
46 'generic' => array('label' => 'Generic Bots', 'icon' => 'visibility', 'color' => '#667eea'),
47 'other' => array('label' => 'Other', 'icon' => 'marker', 'color' => '#667eea'),
48 'unknown' => array('label' => 'Unknown', 'icon' => 'editor-help', 'color' => '#667eea'),
49 'ip_based' => array('label' => 'IP-based', 'icon' => 'networking', 'color' => '#667eea')
50 );
51
52 ?>
53
54 <?php $this->render_layout_open('Bot Statistics', 'bot_statistics', 'Monitor and analyze bot traffic detected on your site.'); ?>
55
56 <!-- Bot Detection Status Card -->
57 <div class="dashboard-card">
58 <h2>Bot Detection Status</h2>
59 <div style="margin-bottom: 20px;">
60 <?php if ($bot_filtering_enabled): ?>
61 <div style="background: rgba(16,185,129,0.1); border: 1px solid var(--dashboard-success); color: var(--dashboard-success); padding: 12px; border-radius: 6px;">
62 <strong>Bot Filtering Active:</strong> <?php echo esc_html($whitelabel_otto_name); ?> is currently disabled for detected bot traffic.
63 </div>
64 <?php else: ?>
65 <div style="background: rgba(245,158,11,0.1); border: 1px solid var(--dashboard-warning); color: var(--dashboard-warning); padding: 12px; border-radius: 6px;">
66 <strong>Bot Filtering Inactive:</strong> Bot detection is tracking bots, but <?php echo esc_html($whitelabel_otto_name); ?> is still processing bot traffic.
67 <a href="?page=<?php echo esc_attr(Metasync_Admin::$page_slug); ?>&tab=general" style="margin-left: 10px;">Enable in Settings →</a>
68 </div>
69 <?php endif; ?>
70 </div>
71 </div>
72
73 <!-- Statistics Overview -->
74 <div class="dashboard-card">
75 <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px;">
76 <div>
77 <h2 style="margin: 0;">Bot Detection Overview</h2>
78 <p style="color: var(--dashboard-text-secondary); margin: 5px 0 0 0;">
79 Overall statistics for bot detection and API optimization.
80 </p>
81 </div>
82 <button type="button" id="reset-bot-stats" class="button" style="color: #ffffff;">
83 <span class="dashicons dashicons-trash" style="font-size:15px;width:15px;height:15px;margin-top:3px;"></span> Reset All Statistics
84 </button>
85 </div>
86
87 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px;">
88 <!-- Total Detections -->
89 <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 20px; border-radius: 8px; color: white;">
90 <div style="font-size: 14px; opacity: 0.9; margin-bottom: 5px;">Total Bot Hits</div>
91 <div style="font-size: 32px; font-weight: 700;"><?php echo number_format($stats['total_detections']); ?></div>
92 </div>
93
94 <!-- API Calls Saved -->
95 <div style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); padding: 20px; border-radius: 8px; color: white;">
96 <div style="font-size: 14px; opacity: 0.9; margin-bottom: 5px;">API Calls Saved</div>
97 <div style="font-size: 32px; font-weight: 700;"><?php echo number_format($stats['api_calls_saved']); ?></div>
98 </div>
99
100 <!-- Unique Bots -->
101 <div style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); padding: 20px; border-radius: 8px; color: white;">
102 <div style="font-size: 14px; opacity: 0.9; margin-bottom: 5px;">Unique Bots Seen</div>
103 <div style="font-size: 32px; font-weight: 700;"><?php echo number_format(count($recent_requests)); ?></div>
104 </div>
105 </div>
106 </div>
107
108 <!-- Bot Type Breakdown -->
109 <div class="dashboard-card">
110 <h2>Breakdown by Bot Type</h2>
111 <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">
112 Distribution of detected bots by category.
113 </p>
114
115 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
116 <?php foreach ($breakdown_labels as $type => $info):
117 if ($type === 'ip_based') continue; // ip_based rolls into "other" in stats
118 $count = $stats['breakdown'][$type] ?? 0;
119 ?>
120 <div style="border: 1px solid var(--dashboard-border); padding: 15px; border-radius: 8px; text-align: center;">
121 <div style="width:36px;height:36px;border-radius:8px;background:var(--dashboard-gradient-primary);display:flex;align-items:center;justify-content:center;margin:0 auto 8px;"><span class="dashicons dashicons-<?php echo esc_attr($info['icon']); ?>" style="font-size:18px;width:18px;height:18px;color:#fff;"></span></div>
122 <div style="font-size: 20px; font-weight: 700; color: var(--dashboard-text-primary);"><?php echo number_format($count); ?></div>
123 <div style="font-size: 12px; color: var(--dashboard-text-secondary); margin-top: 5px;"><?php echo esc_html($info['label']); ?></div>
124 </div>
125 <?php endforeach; ?>
126 </div>
127 </div>
128
129 <!-- Unique Bot Entries -->
130 <div class="dashboard-card">
131 <h2>Unique Bot Entries (Last 100)</h2>
132 <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">
133 Each row is a unique bot+IP combination. The <strong>Hits</strong> column shows how many times that bot visited your site.
134 </p>
135
136 <?php if (empty($recent_requests)): ?>
137 <div style="background: var(--dashboard-card-bg); border: 1px solid var(--dashboard-border, #e2e8f0); padding: 30px; border-radius: 8px; text-align: center; color: var(--dashboard-text-secondary);">
138 <div style="width:56px;height:56px;border-radius:12px;background:var(--dashboard-gradient-primary);display:flex;align-items:center;justify-content:center;margin:0 auto 12px;"><span class="dashicons dashicons-visibility" style="font-size:28px;width:28px;height:28px;color:#fff;"></span></div>
139 <p style="margin: 0;">No bot requests detected yet. Bot detection will automatically log requests when bots visit your site.</p>
140 </div>
141 <?php else: ?>
142 <div style="overflow-x: auto;">
143 <table class="wp-list-table widefat striped" style="width: 100%; border-collapse: collapse; min-width: 1150px;">
144 <thead>
145 <tr>
146 <th style="padding: 12px; text-align: left; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary); min-width: 70px; white-space: nowrap;">Hits</th>
147 <th style="padding: 12px; text-align: left; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary); min-width: 140px; white-space: nowrap;">Bot Name</th>
148 <th style="padding: 12px; text-align: left; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary); min-width: 110px; white-space: nowrap;">Type</th>
149 <th style="padding: 12px; text-align: left; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary); min-width: 220px; white-space: nowrap;">User Agent</th>
150 <th style="padding: 12px; text-align: left; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary); min-width: 130px; white-space: nowrap;">IP Address</th>
151 <th style="padding: 12px; text-align: left; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary); min-width: 150px; white-space: nowrap;">First Seen</th>
152 <th style="padding: 12px; text-align: left; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary); min-width: 150px; white-space: nowrap;">Last Seen</th>
153 <th style="padding: 12px; text-align: left; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary); min-width: 180px; white-space: nowrap;">Last URL</th>
154 </tr>
155 </thead>
156 <tbody>
157 <?php foreach ($recent_requests as $request):
158 $bot_type_info = $breakdown_labels[$request['bot_type']] ?? array('icon' => '❓', 'color' => '#9e9e9e');
159 $hit_count = (int)($request['hit_count'] ?? 1);
160 ?>
161 <tr>
162 <td style="padding: 12px; text-align: center;">
163 <span style="display: inline-block; min-width: 40px; padding: 4px 10px; border-radius: 12px; font-size: 13px; font-weight: 700;
164 <?php if ($hit_count >= 100): ?>
165 background: #ef444420; color: #ef4444;
166 <?php elseif ($hit_count >= 10): ?>
167 background: #f59e0b20; color: #f59e0b;
168 <?php else: ?>
169 background: #10b98120; color: #10b981;
170 <?php endif; ?>
171 ">
172 <?php echo number_format($hit_count); ?>
173 </span>
174 </td>
175 <td style="padding: 12px; color: var(--dashboard-text-primary); font-weight: 600;">
176 <?php echo esc_html($request['bot_name']); ?>
177 </td>
178 <td style="padding: 12px;">
179 <span style="display: inline-flex; align-items:center; gap:4px; padding: 4px 8px; border-radius: 4px; font-size: 12px; background: rgba(102,126,234,0.15); color: var(--dashboard-text-secondary); font-weight: 600;">
180 <span class="dashicons dashicons-<?php echo esc_attr($bot_type_info['icon']); ?>" style="font-size:13px;width:13px;height:13px;"></span>
181 <?php echo esc_html(ucfirst(str_replace('_', ' ', $request['bot_type']))); ?>
182 </span>
183 </td>
184 <td style="padding: 12px; color: var(--dashboard-text-secondary); font-family: monospace; font-size: 11px; max-width: 250px;">
185 <div style="display: flex; align-items: center; gap: 8px;">
186 <span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;" title="<?php echo esc_attr($request['user_agent']); ?>">
187 <?php echo esc_html($request['user_agent']); ?>
188 </span>
189 <button type="button" class="copy-btn" data-copy="<?php echo esc_attr($request['user_agent']); ?>" aria-label="Copy User Agent">
190 <svg class="copy-icon" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
191 <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
192 <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
193 </svg>
194 <svg class="check-icon" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
195 <polyline points="20 6 9 17 4 12"></polyline>
196 </svg>
197 <span class="copy-tooltip">Copy</span>
198 </button>
199 </div>
200 </td>
201 <td style="padding: 12px; color: var(--dashboard-text-secondary); font-family: monospace; font-size: 12px;">
202 <?php echo esc_html($request['ip_address'] ?: 'N/A'); ?>
203 </td>
204 <td style="padding: 12px; color: var(--dashboard-text-secondary); font-size: 12px;">
205 <?php echo esc_html($request['first_seen_at'] ? gmdate('Y-m-d H:i', strtotime($request['first_seen_at'])) : 'N/A'); ?>
206 </td>
207 <td style="padding: 12px; color: var(--dashboard-text-primary); font-size: 12px; font-weight: 500;">
208 <?php echo esc_html($request['last_seen_at'] ? gmdate('Y-m-d H:i', strtotime($request['last_seen_at'])) : 'N/A'); ?>
209 </td>
210 <td style="padding: 12px; color: var(--dashboard-text-secondary); font-size: 11px; max-width: 180px;">
211 <div style="display: flex; align-items: center; gap: 8px;">
212 <span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;" title="<?php echo esc_attr($request['url']); ?>">
213 <?php echo esc_html($request['url'] ?: 'N/A'); ?>
214 </span>
215 <?php if (!empty($request['url'])): ?>
216 <button type="button" class="copy-btn" data-copy="<?php echo esc_attr($request['url']); ?>" aria-label="Copy URL">
217 <svg class="copy-icon" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
218 <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
219 <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
220 </svg>
221 <svg class="check-icon" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
222 <polyline points="20 6 9 17 4 12"></polyline>
223 </svg>
224 <span class="copy-tooltip">Copy</span>
225 </button>
226 <?php endif; ?>
227 </div>
228 </td>
229 </tr>
230 <?php endforeach; ?>
231 </tbody>
232 </table>
233 </div>
234 <?php endif; ?>
235 </div>
236 <?php $this->render_layout_close(); ?>
237
238
239 <style>
240 .dashboard-card table tr:hover {
241 background-color: rgba(0, 0, 0, 0.02);
242 }
243 [data-theme="dark"] .dashboard-card table tr:hover {
244 background-color: rgba(255, 255, 255, 0.05);
245 }
246 .dashboard-card table td {
247 border-bottom: 1px solid rgba(0, 0, 0, 0.05);
248 }
249 [data-theme="dark"] .dashboard-card table td {
250 border-bottom: 1px solid rgba(255, 255, 255, 0.05);
251 }
252
253 /* Copy Button Styles */
254 .copy-btn {
255 position: relative;
256 display: inline-flex;
257 align-items: center;
258 justify-content: center;
259 width: 28px;
260 height: 28px;
261 padding: 0;
262 border: 1px solid var(--dashboard-border, #e2e8f0);
263 border-radius: 6px;
264 background: var(--dashboard-card-bg, #ffffff);
265 color: var(--dashboard-text-secondary, #64748b);
266 cursor: pointer;
267 flex-shrink: 0;
268 transition: all 0.2s ease;
269 }
270 .copy-btn:hover {
271 background: var(--dashboard-hover-bg, #f1f5f9);
272 border-color: var(--dashboard-text-secondary, #94a3b8);
273 color: var(--dashboard-text-primary, #1e293b);
274 }
275 .copy-btn:active {
276 transform: scale(0.95);
277 }
278 .copy-btn:focus {
279 outline: none;
280 box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
281 }
282
283 /* Icon visibility states */
284 .copy-btn .copy-icon {
285 display: block;
286 }
287 .copy-btn .check-icon {
288 display: none;
289 color: #10b981;
290 }
291 .copy-btn.copied .copy-icon {
292 display: none;
293 }
294 .copy-btn.copied .check-icon {
295 display: block;
296 }
297 .copy-btn.copied {
298 border-color: #10b981;
299 background: rgba(16, 185, 129, 0.1);
300 }
301
302 /* Tooltip */
303 .copy-tooltip {
304 position: absolute;
305 bottom: calc(100% + 6px);
306 left: 50%;
307 transform: translateX(-50%);
308 padding: 4px 8px;
309 font-size: 11px;
310 font-weight: 500;
311 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
312 color: #fff;
313 background: #1e293b;
314 border-radius: 4px;
315 white-space: nowrap;
316 opacity: 0;
317 visibility: hidden;
318 transition: opacity 0.2s ease, visibility 0.2s ease;
319 pointer-events: none;
320 z-index: 10;
321 }
322 .copy-tooltip::after {
323 content: '';
324 position: absolute;
325 top: 100%;
326 left: 50%;
327 transform: translateX(-50%);
328 border: 4px solid transparent;
329 border-top-color: #1e293b;
330 }
331 .copy-btn:hover .copy-tooltip,
332 .copy-btn.copied .copy-tooltip {
333 opacity: 1;
334 visibility: visible;
335 }
336
337 /* Dark theme adjustments */
338 [data-theme="dark"] .copy-btn {
339 background: rgba(255, 255, 255, 0.05);
340 border-color: rgba(255, 255, 255, 0.1);
341 color: rgba(255, 255, 255, 0.6);
342 }
343 [data-theme="dark"] .copy-btn:hover {
344 background: rgba(255, 255, 255, 0.1);
345 border-color: rgba(255, 255, 255, 0.2);
346 color: rgba(255, 255, 255, 0.9);
347 }
348 [data-theme="dark"] .copy-btn.copied {
349 border-color: #10b981;
350 background: rgba(16, 185, 129, 0.15);
351 }
352 [data-theme="dark"] .copy-tooltip {
353 background: #f1f5f9;
354 color: #1e293b;
355 }
356 [data-theme="dark"] .copy-tooltip::after {
357 border-top-color: #f1f5f9;
358 }
359 </style>
360