diff --git a/geoip_shop_manager.py b/geoip_shop_manager.py index e3932c1..dd47b0b 100644 --- a/geoip_shop_manager.py +++ b/geoip_shop_manager.py @@ -771,7 +771,9 @@ $bot_hash = md5($detected_bot); // === STEP 1: Check if this bot-type is banned === $ban_file = "$bans_dir/$bot_hash.ban"; if (file_exists($ban_file)) {{ - $ban_until = (int)@file_get_contents($ban_file); + $ban_content = @file_get_contents($ban_file); + $ban_parts = explode('|', $ban_content, 2); + $ban_until = (int)$ban_parts[0]; if (time() < $ban_until) {{ // Bot-type is banned - log and block $timestamp = date('Y-m-d H:i:s'); @@ -823,9 +825,9 @@ if (file_exists($count_file)) {{ // === STEP 3: Check if limit exceeded === if ($count > $rate_limit) {{ - // Create ban for this bot-type + // Create ban for this bot-type (store timestamp|botname) $ban_until = $current_time + $ban_duration; - @file_put_contents($ban_file, $ban_until, LOCK_EX); + @file_put_contents($ban_file, "$ban_until|$detected_bot", LOCK_EX); // Log the ban $timestamp = date('Y-m-d H:i:s'); @@ -847,7 +849,9 @@ $uri = $_SERVER['REQUEST_URI'] ?? '/'; if (rand(1, $cleanup_probability) === 1) {{ $now = time(); foreach (glob("$bans_dir/*.ban") as $f) {{ - $ban_time = (int)@file_get_contents($f); + $ban_content = @file_get_contents($f); + $ban_parts = explode('|', $ban_content, 2); + $ban_time = (int)$ban_parts[0]; if ($now > $ban_time) @unlink($f); }} foreach (glob("$counts_dir/*.count") as $f) {{ @@ -1680,12 +1684,13 @@ def get_shop_log_stats(shop): if ban_file.endswith('.ban'): try: with open(os.path.join(bans_dir, ban_file), 'r') as f: - ban_until = int(f.read().strip()) + content = f.read().strip() + parts = content.split('|', 1) + ban_until = int(parts[0]) + bot_name = parts[1] if len(parts) > 1 else "Unbekannt" if now < ban_until: active_bans += 1 - # Try to find bot name from hash - bot_hash = ban_file.replace('.ban', '') - banned_bots.append(bot_hash[:8]) + banned_bots.append(bot_name) except: pass @@ -1723,6 +1728,8 @@ def show_logs(shop): if rate_limit is not None and ban_duration is not None: print(f"🚦 Rate-Limit: {rate_limit} req/min PRO BOT-TYP, Ban: {ban_duration // 60} min") print(f"🚫 Bans: {total_bans} ausgelöst, {active_bans} Bot-Typen aktuell gebannt") + if banned_bots: + print(f" Gebannt: {', '.join(banned_bots)}") if bots: print(f"\n🤖 Bot-Statistik (nach Bot-Typ):") @@ -1758,6 +1765,7 @@ def show_all_logs(): total_log_entries = 0 total_bans = 0 total_active_bans = 0 + all_banned_bots = [] shop_stats = {} all_ips = {} all_bots = {} @@ -1768,6 +1776,7 @@ def show_all_logs(): total_log_entries += entries total_bans += bans total_active_bans += active_bans + all_banned_bots.extend(banned_bots) if activation_time: runtime_minutes = (datetime.now() - activation_time).total_seconds() / 60 @@ -1783,7 +1792,8 @@ def show_all_logs(): 'ips': ips, 'bots': bots, 'bans': bans, - 'active_bans': active_bans + 'active_bans': active_bans, + 'banned_bots': banned_bots } if runtime_minutes > total_minutes: @@ -1804,6 +1814,11 @@ def show_all_logs(): total_req_min = total_log_entries / total_minutes if total_minutes > 0 else 0 + # Zeige aktuell gebannte Bot-Typen ganz oben + unique_banned_bots = list(set(all_banned_bots)) + if unique_banned_bots: + print(f"\n🚫 AKTUELL GEBANNTE BOT-TYPEN: {', '.join(sorted(unique_banned_bots))}") + print(f"\n📝 Log-Einträge gesamt: {total_log_entries} (⌀ {total_req_min:.1f} req/min, Laufzeit: {format_duration(total_minutes)})") if shop_stats: for shop in sorted(shop_stats.keys()): @@ -1827,21 +1842,14 @@ def show_all_logs(): print(f" ├─ {shop_colored} {geo_icon} {mode_icon}: {count} ({req_min:.1f} req/min, seit {runtime_str}) {bar}") - shop_ips = stats['ips'] - if shop_ips and count > 0: - top_ip = max(shop_ips.items(), key=lambda x: x[1]['count']) - top_ip_addr = top_ip[0] - top_ip_count = top_ip[1]['count'] - top_ip_ua = top_ip[1]['ua'] - top_ip_bot = top_ip[1].get('bot') or detect_bot(top_ip_ua) - top_ip_req_min = top_ip_count / runtime if runtime > 0 else 0 - - if top_ip_bot == 'Unbekannt': - display_name = (top_ip_ua[:40] + '...') if len(top_ip_ua) > 43 else top_ip_ua - else: - display_name = top_ip_bot - - print(f" │ └─➤ Top: {top_ip_addr} ({display_name}) - {top_ip_count}x, {top_ip_req_min:.1f} req/min") + # Top 3 Bot-Typen für diesen Shop + shop_bots = stats['bots'] + if shop_bots and count > 0: + top_bots = sorted(shop_bots.items(), key=lambda x: x[1], reverse=True)[:3] + for i, (bot_name, bot_count) in enumerate(top_bots): + bot_req_min = bot_count / runtime if runtime > 0 else 0 + prefix = "└─➤" if i == len(top_bots) - 1 else "├─➤" + print(f" │ {prefix} {bot_name}: {bot_count}x, {bot_req_min:.1f} req/min") if total_bans > 0 or total_active_bans > 0: print(f"\n🚫 Rate-Limit Bans: {total_bans} ausgelöst, {total_active_bans} Bot-Typen aktuell gebannt") @@ -1856,6 +1864,11 @@ def show_all_logs(): bar = "█" * min(stats['bans'] // 2, 20) if stats['bans'] > 0 else "" print(f" ├─ {shop_colored}: {stats['bans']} bans ({stats['active_bans']} Bot-Typen aktiv) {bar}") + + # Zeige gebannte Bot-Typen + if stats['banned_bots']: + for bot_name in stats['banned_bots']: + print(f" │ └─🚫 {bot_name}") if all_bots: print(f"\n🤖 Bot-Statistik nach Bot-Typ (alle Shops):") @@ -2005,7 +2018,7 @@ def main(): shop_mode = get_shop_mode(shop) mode_icon = get_mode_icon(shop_mode) bot_mode = is_bot_mode(shop_mode) - entries, _, bots, activation_time, total_bans, active_bans, _ = get_shop_log_stats(shop) + entries, _, bots, activation_time, total_bans, active_bans, banned_bots = get_shop_log_stats(shop) runtime = (datetime.now() - activation_time).total_seconds() / 60 if activation_time else 0 httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs') @@ -2015,7 +2028,12 @@ def main(): if bot_mode: rate_limit, ban_duration = get_shop_rate_limit_config(shop) rl_str = f", {rate_limit} req/min/Bot-Typ" if rate_limit else "" - ban_str = f", {active_bans} Bot-Typen gebannt" if active_bans > 0 else "" + ban_str = "" + if active_bans > 0: + if banned_bots: + ban_str = f", 🚫 {', '.join(banned_bots)}" + else: + ban_str = f", {active_bans} Bot-Typen gebannt" print(f" {entries} log entries, {format_duration(runtime)}, {len(BOT_PATTERNS)} Patterns{rl_str}{ban_str}") else: valid, count, _ = validate_existing_cache(httpdocs, get_shop_geo_region(shop))