PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.17
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.17
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.6.17, at views/metasync-otto-bot-statistics.php

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