diff --git a/jtl-wafi-agent.py b/jtl-wafi-agent.py index fdb377e..0ff2988 100644 --- a/jtl-wafi-agent.py +++ b/jtl-wafi-agent.py @@ -37,7 +37,7 @@ from logging.handlers import RotatingFileHandler # ============================================================================= # VERSION # ============================================================================= -VERSION = "2.4.0" +VERSION = "2.5.0" # ============================================================================= # PFADE - AGENT @@ -83,12 +83,14 @@ DEFAULT_RATE_LIMIT = 30 # Requests pro Minute DEFAULT_BAN_DURATION = 5 # Minuten # ============================================================================= -# CACHE VALIDATION +# COUNTRY PRESETS (ersetzt GEO_REGIONS) # ============================================================================= -MIN_RANGES = { - "dach": 1000, - "eurozone": 5000 -} +COUNTRY_PRESET_EU_PLUS = [ + "de", "at", "ch", "be", "cy", "ee", "es", "fi", "fr", "gb", + "gr", "hr", "ie", "it", "lt", "lu", "lv", "mt", "nl", "pt", "si", "sk" +] + +COUNTRY_PRESET_DACH = ["de", "at", "ch"] # ============================================================================= # LINK11 @@ -101,35 +103,206 @@ LINK11_IP = "128.65.223.106" DNS_CACHE = {} # ============================================================================= -# GEO REGIONS +# COUNTRIES - Vollstรคndige Lรคnderliste (ISO 3166-1 Alpha-2) # ============================================================================= -GEO_REGIONS = { - "dach": { - "name": "DACH", - "countries": ["de", "at", "ch"], - "description": "Deutschland, ร–sterreich, Schweiz", - "icon": "๐Ÿ‡ฉ๐Ÿ‡ช๐Ÿ‡ฆ๐Ÿ‡น๐Ÿ‡จ๐Ÿ‡ญ", - "short": "DACH" - }, - "eurozone": { - "name": "Eurozone + GB", - "countries": [ - "de", "at", "ch", "be", "cy", "ee", "es", "fi", "fr", "gb", - "gr", "hr", "ie", "it", "lt", "lu", "lv", "mt", "nl", "pt", "si", "sk" - ], - "description": "22 Lรคnder: DE, AT, CH, BE, CY, EE, ES, FI, FR, GB, GR, HR, IE, IT, LT, LU, LV, MT, NL, PT, SI, SK", - "icon": "๐Ÿ‡ช๐Ÿ‡บ", - "short": "EU+" - }, - "none": { - "name": "Bot-Only", - "countries": [], - "description": "Nur Bot-Rate-Limiting, weltweit erreichbar", - "icon": "๐Ÿค–", - "short": "BOT" - } +COUNTRIES = { + "ad": {"name": "Andorra", "flag": "๐Ÿ‡ฆ๐Ÿ‡ฉ"}, + "ae": {"name": "VAE", "flag": "๐Ÿ‡ฆ๐Ÿ‡ช"}, + "af": {"name": "Afghanistan", "flag": "๐Ÿ‡ฆ๐Ÿ‡ซ"}, + "ag": {"name": "Antigua und Barbuda", "flag": "๐Ÿ‡ฆ๐Ÿ‡ฌ"}, + "al": {"name": "Albanien", "flag": "๐Ÿ‡ฆ๐Ÿ‡ฑ"}, + "am": {"name": "Armenien", "flag": "๐Ÿ‡ฆ๐Ÿ‡ฒ"}, + "ao": {"name": "Angola", "flag": "๐Ÿ‡ฆ๐Ÿ‡ด"}, + "ar": {"name": "Argentinien", "flag": "๐Ÿ‡ฆ๐Ÿ‡ท"}, + "at": {"name": "ร–sterreich", "flag": "๐Ÿ‡ฆ๐Ÿ‡น"}, + "au": {"name": "Australien", "flag": "๐Ÿ‡ฆ๐Ÿ‡บ"}, + "az": {"name": "Aserbaidschan", "flag": "๐Ÿ‡ฆ๐Ÿ‡ฟ"}, + "ba": {"name": "Bosnien-Herzegowina", "flag": "๐Ÿ‡ง๐Ÿ‡ฆ"}, + "bb": {"name": "Barbados", "flag": "๐Ÿ‡ง๐Ÿ‡ง"}, + "bd": {"name": "Bangladesch", "flag": "๐Ÿ‡ง๐Ÿ‡ฉ"}, + "be": {"name": "Belgien", "flag": "๐Ÿ‡ง๐Ÿ‡ช"}, + "bf": {"name": "Burkina Faso", "flag": "๐Ÿ‡ง๐Ÿ‡ซ"}, + "bg": {"name": "Bulgarien", "flag": "๐Ÿ‡ง๐Ÿ‡ฌ"}, + "bh": {"name": "Bahrain", "flag": "๐Ÿ‡ง๐Ÿ‡ญ"}, + "bi": {"name": "Burundi", "flag": "๐Ÿ‡ง๐Ÿ‡ฎ"}, + "bj": {"name": "Benin", "flag": "๐Ÿ‡ง๐Ÿ‡ฏ"}, + "bn": {"name": "Brunei", "flag": "๐Ÿ‡ง๐Ÿ‡ณ"}, + "bo": {"name": "Bolivien", "flag": "๐Ÿ‡ง๐Ÿ‡ด"}, + "br": {"name": "Brasilien", "flag": "๐Ÿ‡ง๐Ÿ‡ท"}, + "bs": {"name": "Bahamas", "flag": "๐Ÿ‡ง๐Ÿ‡ธ"}, + "bt": {"name": "Bhutan", "flag": "๐Ÿ‡ง๐Ÿ‡น"}, + "bw": {"name": "Botswana", "flag": "๐Ÿ‡ง๐Ÿ‡ผ"}, + "by": {"name": "Belarus", "flag": "๐Ÿ‡ง๐Ÿ‡พ"}, + "bz": {"name": "Belize", "flag": "๐Ÿ‡ง๐Ÿ‡ฟ"}, + "ca": {"name": "Kanada", "flag": "๐Ÿ‡จ๐Ÿ‡ฆ"}, + "cd": {"name": "DR Kongo", "flag": "๐Ÿ‡จ๐Ÿ‡ฉ"}, + "cf": {"name": "Zentralafrikanische Republik", "flag": "๐Ÿ‡จ๐Ÿ‡ซ"}, + "cg": {"name": "Kongo", "flag": "๐Ÿ‡จ๐Ÿ‡ฌ"}, + "ch": {"name": "Schweiz", "flag": "๐Ÿ‡จ๐Ÿ‡ญ"}, + "ci": {"name": "Elfenbeinkรผste", "flag": "๐Ÿ‡จ๐Ÿ‡ฎ"}, + "cl": {"name": "Chile", "flag": "๐Ÿ‡จ๐Ÿ‡ฑ"}, + "cm": {"name": "Kamerun", "flag": "๐Ÿ‡จ๐Ÿ‡ฒ"}, + "cn": {"name": "China", "flag": "๐Ÿ‡จ๐Ÿ‡ณ"}, + "co": {"name": "Kolumbien", "flag": "๐Ÿ‡จ๐Ÿ‡ด"}, + "cr": {"name": "Costa Rica", "flag": "๐Ÿ‡จ๐Ÿ‡ท"}, + "cu": {"name": "Kuba", "flag": "๐Ÿ‡จ๐Ÿ‡บ"}, + "cv": {"name": "Kap Verde", "flag": "๐Ÿ‡จ๐Ÿ‡ป"}, + "cy": {"name": "Zypern", "flag": "๐Ÿ‡จ๐Ÿ‡พ"}, + "cz": {"name": "Tschechien", "flag": "๐Ÿ‡จ๐Ÿ‡ฟ"}, + "de": {"name": "Deutschland", "flag": "๐Ÿ‡ฉ๐Ÿ‡ช"}, + "dj": {"name": "Dschibuti", "flag": "๐Ÿ‡ฉ๐Ÿ‡ฏ"}, + "dk": {"name": "Dรคnemark", "flag": "๐Ÿ‡ฉ๐Ÿ‡ฐ"}, + "dm": {"name": "Dominica", "flag": "๐Ÿ‡ฉ๐Ÿ‡ฒ"}, + "do": {"name": "Dominikanische Republik", "flag": "๐Ÿ‡ฉ๐Ÿ‡ด"}, + "dz": {"name": "Algerien", "flag": "๐Ÿ‡ฉ๐Ÿ‡ฟ"}, + "ec": {"name": "Ecuador", "flag": "๐Ÿ‡ช๐Ÿ‡จ"}, + "ee": {"name": "Estland", "flag": "๐Ÿ‡ช๐Ÿ‡ช"}, + "eg": {"name": "ร„gypten", "flag": "๐Ÿ‡ช๐Ÿ‡ฌ"}, + "er": {"name": "Eritrea", "flag": "๐Ÿ‡ช๐Ÿ‡ท"}, + "es": {"name": "Spanien", "flag": "๐Ÿ‡ช๐Ÿ‡ธ"}, + "et": {"name": "ร„thiopien", "flag": "๐Ÿ‡ช๐Ÿ‡น"}, + "fi": {"name": "Finnland", "flag": "๐Ÿ‡ซ๐Ÿ‡ฎ"}, + "fj": {"name": "Fidschi", "flag": "๐Ÿ‡ซ๐Ÿ‡ฏ"}, + "fr": {"name": "Frankreich", "flag": "๐Ÿ‡ซ๐Ÿ‡ท"}, + "ga": {"name": "Gabun", "flag": "๐Ÿ‡ฌ๐Ÿ‡ฆ"}, + "gb": {"name": "GroรŸbritannien", "flag": "๐Ÿ‡ฌ๐Ÿ‡ง"}, + "gd": {"name": "Grenada", "flag": "๐Ÿ‡ฌ๐Ÿ‡ฉ"}, + "ge": {"name": "Georgien", "flag": "๐Ÿ‡ฌ๐Ÿ‡ช"}, + "gh": {"name": "Ghana", "flag": "๐Ÿ‡ฌ๐Ÿ‡ญ"}, + "gm": {"name": "Gambia", "flag": "๐Ÿ‡ฌ๐Ÿ‡ฒ"}, + "gn": {"name": "Guinea", "flag": "๐Ÿ‡ฌ๐Ÿ‡ณ"}, + "gq": {"name": "ร„quatorialguinea", "flag": "๐Ÿ‡ฌ๐Ÿ‡ถ"}, + "gr": {"name": "Griechenland", "flag": "๐Ÿ‡ฌ๐Ÿ‡ท"}, + "gt": {"name": "Guatemala", "flag": "๐Ÿ‡ฌ๐Ÿ‡น"}, + "gw": {"name": "Guinea-Bissau", "flag": "๐Ÿ‡ฌ๐Ÿ‡ผ"}, + "gy": {"name": "Guyana", "flag": "๐Ÿ‡ฌ๐Ÿ‡พ"}, + "hk": {"name": "Hongkong", "flag": "๐Ÿ‡ญ๐Ÿ‡ฐ"}, + "hn": {"name": "Honduras", "flag": "๐Ÿ‡ญ๐Ÿ‡ณ"}, + "hr": {"name": "Kroatien", "flag": "๐Ÿ‡ญ๐Ÿ‡ท"}, + "ht": {"name": "Haiti", "flag": "๐Ÿ‡ญ๐Ÿ‡น"}, + "hu": {"name": "Ungarn", "flag": "๐Ÿ‡ญ๐Ÿ‡บ"}, + "id": {"name": "Indonesien", "flag": "๐Ÿ‡ฎ๐Ÿ‡ฉ"}, + "ie": {"name": "Irland", "flag": "๐Ÿ‡ฎ๐Ÿ‡ช"}, + "il": {"name": "Israel", "flag": "๐Ÿ‡ฎ๐Ÿ‡ฑ"}, + "in": {"name": "Indien", "flag": "๐Ÿ‡ฎ๐Ÿ‡ณ"}, + "iq": {"name": "Irak", "flag": "๐Ÿ‡ฎ๐Ÿ‡ถ"}, + "ir": {"name": "Iran", "flag": "๐Ÿ‡ฎ๐Ÿ‡ท"}, + "is": {"name": "Island", "flag": "๐Ÿ‡ฎ๐Ÿ‡ธ"}, + "it": {"name": "Italien", "flag": "๐Ÿ‡ฎ๐Ÿ‡น"}, + "jm": {"name": "Jamaika", "flag": "๐Ÿ‡ฏ๐Ÿ‡ฒ"}, + "jo": {"name": "Jordanien", "flag": "๐Ÿ‡ฏ๐Ÿ‡ด"}, + "jp": {"name": "Japan", "flag": "๐Ÿ‡ฏ๐Ÿ‡ต"}, + "ke": {"name": "Kenia", "flag": "๐Ÿ‡ฐ๐Ÿ‡ช"}, + "kg": {"name": "Kirgisistan", "flag": "๐Ÿ‡ฐ๐Ÿ‡ฌ"}, + "kh": {"name": "Kambodscha", "flag": "๐Ÿ‡ฐ๐Ÿ‡ญ"}, + "km": {"name": "Komoren", "flag": "๐Ÿ‡ฐ๐Ÿ‡ฒ"}, + "kn": {"name": "St. Kitts und Nevis", "flag": "๐Ÿ‡ฐ๐Ÿ‡ณ"}, + "kp": {"name": "Nordkorea", "flag": "๐Ÿ‡ฐ๐Ÿ‡ต"}, + "kr": {"name": "Sรผdkorea", "flag": "๐Ÿ‡ฐ๐Ÿ‡ท"}, + "kw": {"name": "Kuwait", "flag": "๐Ÿ‡ฐ๐Ÿ‡ผ"}, + "kz": {"name": "Kasachstan", "flag": "๐Ÿ‡ฐ๐Ÿ‡ฟ"}, + "la": {"name": "Laos", "flag": "๐Ÿ‡ฑ๐Ÿ‡ฆ"}, + "lb": {"name": "Libanon", "flag": "๐Ÿ‡ฑ๐Ÿ‡ง"}, + "lc": {"name": "St. Lucia", "flag": "๐Ÿ‡ฑ๐Ÿ‡จ"}, + "li": {"name": "Liechtenstein", "flag": "๐Ÿ‡ฑ๐Ÿ‡ฎ"}, + "lk": {"name": "Sri Lanka", "flag": "๐Ÿ‡ฑ๐Ÿ‡ฐ"}, + "lr": {"name": "Liberia", "flag": "๐Ÿ‡ฑ๐Ÿ‡ท"}, + "ls": {"name": "Lesotho", "flag": "๐Ÿ‡ฑ๐Ÿ‡ธ"}, + "lt": {"name": "Litauen", "flag": "๐Ÿ‡ฑ๐Ÿ‡น"}, + "lu": {"name": "Luxemburg", "flag": "๐Ÿ‡ฑ๐Ÿ‡บ"}, + "lv": {"name": "Lettland", "flag": "๐Ÿ‡ฑ๐Ÿ‡ป"}, + "ly": {"name": "Libyen", "flag": "๐Ÿ‡ฑ๐Ÿ‡พ"}, + "ma": {"name": "Marokko", "flag": "๐Ÿ‡ฒ๐Ÿ‡ฆ"}, + "mc": {"name": "Monaco", "flag": "๐Ÿ‡ฒ๐Ÿ‡จ"}, + "md": {"name": "Moldau", "flag": "๐Ÿ‡ฒ๐Ÿ‡ฉ"}, + "me": {"name": "Montenegro", "flag": "๐Ÿ‡ฒ๐Ÿ‡ช"}, + "mg": {"name": "Madagaskar", "flag": "๐Ÿ‡ฒ๐Ÿ‡ฌ"}, + "mk": {"name": "Nordmazedonien", "flag": "๐Ÿ‡ฒ๐Ÿ‡ฐ"}, + "ml": {"name": "Mali", "flag": "๐Ÿ‡ฒ๐Ÿ‡ฑ"}, + "mm": {"name": "Myanmar", "flag": "๐Ÿ‡ฒ๐Ÿ‡ฒ"}, + "mn": {"name": "Mongolei", "flag": "๐Ÿ‡ฒ๐Ÿ‡ณ"}, + "mo": {"name": "Macau", "flag": "๐Ÿ‡ฒ๐Ÿ‡ด"}, + "mr": {"name": "Mauretanien", "flag": "๐Ÿ‡ฒ๐Ÿ‡ท"}, + "mt": {"name": "Malta", "flag": "๐Ÿ‡ฒ๐Ÿ‡น"}, + "mu": {"name": "Mauritius", "flag": "๐Ÿ‡ฒ๐Ÿ‡บ"}, + "mv": {"name": "Malediven", "flag": "๐Ÿ‡ฒ๐Ÿ‡ป"}, + "mw": {"name": "Malawi", "flag": "๐Ÿ‡ฒ๐Ÿ‡ผ"}, + "mx": {"name": "Mexiko", "flag": "๐Ÿ‡ฒ๐Ÿ‡ฝ"}, + "my": {"name": "Malaysia", "flag": "๐Ÿ‡ฒ๐Ÿ‡พ"}, + "mz": {"name": "Mosambik", "flag": "๐Ÿ‡ฒ๐Ÿ‡ฟ"}, + "na": {"name": "Namibia", "flag": "๐Ÿ‡ณ๐Ÿ‡ฆ"}, + "ne": {"name": "Niger", "flag": "๐Ÿ‡ณ๐Ÿ‡ช"}, + "ng": {"name": "Nigeria", "flag": "๐Ÿ‡ณ๐Ÿ‡ฌ"}, + "ni": {"name": "Nicaragua", "flag": "๐Ÿ‡ณ๐Ÿ‡ฎ"}, + "nl": {"name": "Niederlande", "flag": "๐Ÿ‡ณ๐Ÿ‡ฑ"}, + "no": {"name": "Norwegen", "flag": "๐Ÿ‡ณ๐Ÿ‡ด"}, + "np": {"name": "Nepal", "flag": "๐Ÿ‡ณ๐Ÿ‡ต"}, + "nz": {"name": "Neuseeland", "flag": "๐Ÿ‡ณ๐Ÿ‡ฟ"}, + "om": {"name": "Oman", "flag": "๐Ÿ‡ด๐Ÿ‡ฒ"}, + "pa": {"name": "Panama", "flag": "๐Ÿ‡ต๐Ÿ‡ฆ"}, + "pe": {"name": "Peru", "flag": "๐Ÿ‡ต๐Ÿ‡ช"}, + "pg": {"name": "Papua-Neuguinea", "flag": "๐Ÿ‡ต๐Ÿ‡ฌ"}, + "ph": {"name": "Philippinen", "flag": "๐Ÿ‡ต๐Ÿ‡ญ"}, + "pk": {"name": "Pakistan", "flag": "๐Ÿ‡ต๐Ÿ‡ฐ"}, + "pl": {"name": "Polen", "flag": "๐Ÿ‡ต๐Ÿ‡ฑ"}, + "pt": {"name": "Portugal", "flag": "๐Ÿ‡ต๐Ÿ‡น"}, + "py": {"name": "Paraguay", "flag": "๐Ÿ‡ต๐Ÿ‡พ"}, + "qa": {"name": "Katar", "flag": "๐Ÿ‡ถ๐Ÿ‡ฆ"}, + "ro": {"name": "Rumรคnien", "flag": "๐Ÿ‡ท๐Ÿ‡ด"}, + "rs": {"name": "Serbien", "flag": "๐Ÿ‡ท๐Ÿ‡ธ"}, + "ru": {"name": "Russland", "flag": "๐Ÿ‡ท๐Ÿ‡บ"}, + "rw": {"name": "Ruanda", "flag": "๐Ÿ‡ท๐Ÿ‡ผ"}, + "sa": {"name": "Saudi-Arabien", "flag": "๐Ÿ‡ธ๐Ÿ‡ฆ"}, + "sb": {"name": "Salomonen", "flag": "๐Ÿ‡ธ๐Ÿ‡ง"}, + "sc": {"name": "Seychellen", "flag": "๐Ÿ‡ธ๐Ÿ‡จ"}, + "sd": {"name": "Sudan", "flag": "๐Ÿ‡ธ๐Ÿ‡ฉ"}, + "se": {"name": "Schweden", "flag": "๐Ÿ‡ธ๐Ÿ‡ช"}, + "sg": {"name": "Singapur", "flag": "๐Ÿ‡ธ๐Ÿ‡ฌ"}, + "si": {"name": "Slowenien", "flag": "๐Ÿ‡ธ๐Ÿ‡ฎ"}, + "sk": {"name": "Slowakei", "flag": "๐Ÿ‡ธ๐Ÿ‡ฐ"}, + "sl": {"name": "Sierra Leone", "flag": "๐Ÿ‡ธ๐Ÿ‡ฑ"}, + "sm": {"name": "San Marino", "flag": "๐Ÿ‡ธ๐Ÿ‡ฒ"}, + "sn": {"name": "Senegal", "flag": "๐Ÿ‡ธ๐Ÿ‡ณ"}, + "so": {"name": "Somalia", "flag": "๐Ÿ‡ธ๐Ÿ‡ด"}, + "sr": {"name": "Suriname", "flag": "๐Ÿ‡ธ๐Ÿ‡ท"}, + "ss": {"name": "Sรผdsudan", "flag": "๐Ÿ‡ธ๐Ÿ‡ธ"}, + "sv": {"name": "El Salvador", "flag": "๐Ÿ‡ธ๐Ÿ‡ป"}, + "sy": {"name": "Syrien", "flag": "๐Ÿ‡ธ๐Ÿ‡พ"}, + "sz": {"name": "Eswatini", "flag": "๐Ÿ‡ธ๐Ÿ‡ฟ"}, + "td": {"name": "Tschad", "flag": "๐Ÿ‡น๐Ÿ‡ฉ"}, + "tg": {"name": "Togo", "flag": "๐Ÿ‡น๐Ÿ‡ฌ"}, + "th": {"name": "Thailand", "flag": "๐Ÿ‡น๐Ÿ‡ญ"}, + "tj": {"name": "Tadschikistan", "flag": "๐Ÿ‡น๐Ÿ‡ฏ"}, + "tl": {"name": "Osttimor", "flag": "๐Ÿ‡น๐Ÿ‡ฑ"}, + "tm": {"name": "Turkmenistan", "flag": "๐Ÿ‡น๐Ÿ‡ฒ"}, + "tn": {"name": "Tunesien", "flag": "๐Ÿ‡น๐Ÿ‡ณ"}, + "to": {"name": "Tonga", "flag": "๐Ÿ‡น๐Ÿ‡ด"}, + "tr": {"name": "Tรผrkei", "flag": "๐Ÿ‡น๐Ÿ‡ท"}, + "tt": {"name": "Trinidad und Tobago", "flag": "๐Ÿ‡น๐Ÿ‡น"}, + "tw": {"name": "Taiwan", "flag": "๐Ÿ‡น๐Ÿ‡ผ"}, + "tz": {"name": "Tansania", "flag": "๐Ÿ‡น๐Ÿ‡ฟ"}, + "ua": {"name": "Ukraine", "flag": "๐Ÿ‡บ๐Ÿ‡ฆ"}, + "ug": {"name": "Uganda", "flag": "๐Ÿ‡บ๐Ÿ‡ฌ"}, + "us": {"name": "USA", "flag": "๐Ÿ‡บ๐Ÿ‡ธ"}, + "uy": {"name": "Uruguay", "flag": "๐Ÿ‡บ๐Ÿ‡พ"}, + "uz": {"name": "Usbekistan", "flag": "๐Ÿ‡บ๐Ÿ‡ฟ"}, + "va": {"name": "Vatikanstadt", "flag": "๐Ÿ‡ป๐Ÿ‡ฆ"}, + "vc": {"name": "St. Vincent und die Grenadinen", "flag": "๐Ÿ‡ป๐Ÿ‡จ"}, + "ve": {"name": "Venezuela", "flag": "๐Ÿ‡ป๐Ÿ‡ช"}, + "vn": {"name": "Vietnam", "flag": "๐Ÿ‡ป๐Ÿ‡ณ"}, + "vu": {"name": "Vanuatu", "flag": "๐Ÿ‡ป๐Ÿ‡บ"}, + "ws": {"name": "Samoa", "flag": "๐Ÿ‡ผ๐Ÿ‡ธ"}, + "xk": {"name": "Kosovo", "flag": "๐Ÿ‡ฝ๐Ÿ‡ฐ"}, + "ye": {"name": "Jemen", "flag": "๐Ÿ‡พ๐Ÿ‡ช"}, + "za": {"name": "Sรผdafrika", "flag": "๐Ÿ‡ฟ๐Ÿ‡ฆ"}, + "zm": {"name": "Sambia", "flag": "๐Ÿ‡ฟ๐Ÿ‡ฒ"}, + "zw": {"name": "Simbabwe", "flag": "๐Ÿ‡ฟ๐Ÿ‡ผ"}, } +# Default Country Rate-Limit Settings +DEFAULT_COUNTRY_RATE_LIMIT = 100 # Requests pro Minute pro Land +DEFAULT_COUNTRY_BAN_DURATION = 600 # Sekunden (10 Minuten) + # ============================================================================= # BOT IP RANGES - Fรผr Bots die sich mit normalem User-Agent tarnen # ============================================================================= @@ -751,23 +924,21 @@ def check_link11(domain: str) -> Dict[str, Any]: return DNS_CACHE[domain] -def get_geo_region_info(geo_region: str) -> Dict[str, Any]: - """Gibt Region-Info zurรผck.""" - return GEO_REGIONS.get(geo_region, GEO_REGIONS["dach"]) - - -def is_bot_mode(mode: str) -> bool: - """Prรผft ob der Modus Bot-Mode ist.""" - return mode == 'bot' +def get_country_preset(preset_name: str) -> List[str]: + """Gibt die Lรคnder eines Presets zurรผck.""" + if preset_name == "eu_plus": + return COUNTRY_PRESET_EU_PLUS + elif preset_name == "dach": + return COUNTRY_PRESET_DACH + return [] # ============================================================================= # PHP TEMPLATE GENERATORS # ============================================================================= -def generate_php_countries_array(geo_region: str) -> str: - """Generiert PHP-Array der erlaubten Lรคnder.""" - region_info = get_geo_region_info(geo_region) - return ", ".join([f"'{c}'" for c in region_info["countries"]]) +def generate_php_unlimited_countries(countries: List[str]) -> str: + """Generiert PHP-Array der unlimitierten Lรคnder.""" + return ", ".join([f"'{c.lower()}'" for c in countries]) def generate_php_bot_patterns() -> str: @@ -797,93 +968,6 @@ def generate_php_bot_ip_ranges() -> str: -# ============================================================================= -# PHP TEMPLATES - GEOIP -# ============================================================================= -GEOIP_SCRIPT_TEMPLATE = ''' $expiry_date) return; - -$visitor_ip = $_SERVER['REMOTE_ADDR'] ?? ''; -if (empty($visitor_ip)) return; -if (filter_var($visitor_ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) return; - -$cache_file = __DIR__ . '/{cache_file}'; -$cache_duration = 86400; -$log_file = __DIR__ . '/{log_file}'; -$min_ranges = {min_ranges}; -$allowed_countries = [{countries_array}]; - -function download_allowed_ranges($countries) {{ - $ranges = []; - foreach ($countries as $country) {{ - $url = "https://www.ipdeny.com/ipblocks/data/aggregated/$country-aggregated.zone"; - $ctx = stream_context_create(['http' => ['timeout' => 30]]); - $content = @file_get_contents($url, false, $ctx); - if ($content !== false) {{ - foreach (explode("\\n", trim($content)) as $line) {{ - $line = trim($line); - if (!empty($line) && strpos($line, '/') !== false) $ranges[] = $line; - }} - }} - }} - return $ranges; -}} - -function ip_in_range($ip, $cidr) {{ - list($subnet, $mask) = explode('/', $cidr); - $mask_long = -1 << (32 - (int)$mask); - return (ip2long($ip) & $mask_long) == (ip2long($subnet) & $mask_long); -}} - -$allowed_ranges = []; -$cache_valid = false; - -if (file_exists($cache_file) && (time() - filemtime($cache_file)) < $cache_duration) {{ - $cached_data = @file_get_contents($cache_file); - if ($cached_data !== false) {{ - $allowed_ranges = @unserialize($cached_data); - if (is_array($allowed_ranges) && count($allowed_ranges) >= $min_ranges) {{ - $cache_valid = true; - }} else {{ - @unlink($cache_file); - $allowed_ranges = []; - }} - }} -}} - -if (!$cache_valid) {{ - $allowed_ranges = download_allowed_ranges($allowed_countries); - if (is_array($allowed_ranges) && count($allowed_ranges) >= $min_ranges) {{ - @file_put_contents($cache_file, serialize($allowed_ranges)); - $cache_valid = true; - }} else {{ - error_log("JTL-WAFi FAIL-OPEN: Could not load valid IP ranges (got " . count($allowed_ranges) . ", need $min_ranges)"); - return; - }} -}} - -$is_allowed = false; -foreach ($allowed_ranges as $range) {{ - if (ip_in_range($visitor_ip, $range)) {{ $is_allowed = true; break; }} -}} - -if (!$is_allowed) {{ - $timestamp = date('Y-m-d H:i:s'); - $ua = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown'; - $uri = $_SERVER['REQUEST_URI'] ?? '/'; - @file_put_contents($log_file, "[$timestamp] IP: $visitor_ip | UA: $ua | URI: $uri\\n", FILE_APPEND | LOCK_EX); - header('HTTP/1.1 403 Forbidden'); - exit; -}} -''' - # ============================================================================= # PHP TEMPLATES - BOT RATE-LIMITING # ============================================================================= @@ -1079,28 +1163,29 @@ return; ''' # ============================================================================= -# PHP TEMPLATES - BOT MONITOR-ONLY (No Blocking) +# PHP TEMPLATES - MONITOR (Full Monitoring without Blocking) # ============================================================================= -BOT_MONITOR_TEMPLATE = ''' $expiry_date) return; $log_file = __DIR__ . '/{log_file}'; -$stats_file = __DIR__ . '/{ratelimit_dir}/monitor_stats.json'; -$stats_dir = __DIR__ . '/{ratelimit_dir}'; +$ratelimit_dir = __DIR__ . '/{ratelimit_dir}'; +$country_cache_dir = $ratelimit_dir . '/country_cache'; $visitor_ip = $_SERVER['REMOTE_ADDR'] ?? ''; $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? ''; -// Ensure stats directory exists -if (!is_dir($stats_dir)) @mkdir($stats_dir, 0777, true); +// Ensure directories exist +if (!is_dir($ratelimit_dir)) @mkdir($ratelimit_dir, 0777, true); +if (!is_dir($country_cache_dir)) @mkdir($country_cache_dir, 0777, true); // === IP-in-CIDR Check Function === function ip_in_cidr($ip, $cidr) {{ @@ -1113,6 +1198,56 @@ function ip_in_cidr($ip, $cidr) {{ return ($ip_long & $mask_long) === ($subnet_long & $mask_long); }} +// === Country Detection via IP === +function get_country_for_ip($ip, $country_cache_dir) {{ + // Most common countries first for performance + $countries = ['de', 'at', 'ch', 'us', 'gb', 'fr', 'nl', 'it', 'es', 'pl', 'be', 'se', 'no', 'dk', 'fi', + 'ru', 'cn', 'jp', 'kr', 'in', 'br', 'au', 'ca', 'ua', 'cz', 'pt', 'ie', 'gr', 'hu', 'ro', + 'bg', 'hr', 'sk', 'si', 'lt', 'lv', 'ee', 'lu', 'mt', 'cy', 'tr', 'il', 'za', 'mx', 'ar', + 'cl', 'co', 'pe', 've', 'eg', 'ng', 'ke', 'ma', 'tn', 'pk', 'bd', 'vn', 'th', 'my', 'sg', + 'id', 'ph', 'tw', 'hk', 'nz', 'ae', 'sa', 'qa', 'kw', 'bh', 'om', 'ir', 'iq']; + + foreach ($countries as $country) {{ + $cache_file = "$country_cache_dir/$country.ranges"; + $ranges = []; + + // Load from cache or download + if (file_exists($cache_file) && (time() - filemtime($cache_file)) < 86400) {{ + $ranges = @unserialize(@file_get_contents($cache_file)); + }} + + if (empty($ranges) || !is_array($ranges)) {{ + // Download fresh from ipdeny.com + $url = "https://www.ipdeny.com/ipblocks/data/aggregated/$country-aggregated.zone"; + $ctx = stream_context_create(['http' => ['timeout' => 10]]); + $content = @file_get_contents($url, false, $ctx); + if ($content !== false) {{ + $ranges = []; + foreach (explode("\\n", trim($content)) as $line) {{ + $line = trim($line); + if (!empty($line) && strpos($line, '/') !== false) {{ + $ranges[] = $line; + }} + }} + if (count($ranges) > 100) {{ + @file_put_contents($cache_file, serialize($ranges)); + }} + }} + }} + + // Check if IP is in this country + if (!empty($ranges)) {{ + foreach ($ranges as $range) {{ + if (ip_in_cidr($ip, $range)) {{ + return strtoupper($country); + }} + }} + }} + }} + + return 'XX'; // Unknown country +}} + // === Bot IP Ranges (fรผr getarnte Bots) === $bot_ip_ranges = [ {bot_ip_ranges} @@ -1125,7 +1260,7 @@ $bot_patterns = [ $generic_patterns = [{generic_patterns}]; -// === STEP 0: IP-basierte Bot-Erkennung (hรถchste Prioritรคt) === +// === Detect Bot === $detected_bot = null; if (!empty($visitor_ip)) {{ @@ -1139,7 +1274,6 @@ if (!empty($visitor_ip)) {{ }} }} -// === STEP 1: User-Agent-basierte Erkennung (falls IP nicht erkannt) === if ($detected_bot === null && !empty($user_agent)) {{ foreach ($bot_patterns as $bot_name => $pattern) {{ if (preg_match($pattern, $user_agent)) {{ @@ -1159,131 +1293,401 @@ if ($detected_bot === null && !empty($user_agent)) {{ }} }} -// Not a bot - allow through without logging -if ($detected_bot === null) return; +// === Detect Country === +$country = 'XX'; +if (!empty($visitor_ip) && filter_var($visitor_ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {{ + $country = get_country_for_ip($visitor_ip, $country_cache_dir); +}} -// === STEP 2: Log the bot request (MONITOR ONLY - no blocking!) === +// === Log Entry === $timestamp = date('Y-m-d H:i:s'); $uri = $_SERVER['REQUEST_URI'] ?? '/'; -@file_put_contents($log_file, "[$timestamp] MONITOR: $detected_bot | IP: $visitor_ip | URI: $uri\\n", FILE_APPEND | LOCK_EX); -// === STEP 3: Update simple stats for dashboard === -$stats = []; -if (file_exists($stats_file)) {{ - $stats = @json_decode(@file_get_contents($stats_file), true) ?: []; +if ($detected_bot !== null) {{ + @file_put_contents($log_file, "[$timestamp] MONITOR_BOT: $detected_bot | IP: $visitor_ip | Country: $country | URI: $uri\\n", FILE_APPEND | LOCK_EX); +}} else {{ + @file_put_contents($log_file, "[$timestamp] MONITOR_HUMAN | IP: $visitor_ip | Country: $country | URI: $uri\\n", FILE_APPEND | LOCK_EX); }} -$minute_key = date('Y-m-d H:i'); -if (!isset($stats['by_minute'])) $stats['by_minute'] = []; -if (!isset($stats['by_minute'][$minute_key])) $stats['by_minute'][$minute_key] = []; -if (!isset($stats['by_minute'][$minute_key][$detected_bot])) $stats['by_minute'][$minute_key][$detected_bot] = 0; -$stats['by_minute'][$minute_key][$detected_bot]++; - -// Cleanup old minutes (keep last 60) -$minutes = array_keys($stats['by_minute']); -if (count($minutes) > 60) {{ - sort($minutes); - $to_remove = array_slice($minutes, 0, count($minutes) - 60); - foreach ($to_remove as $m) unset($stats['by_minute'][$m]); -}} - -@file_put_contents($stats_file, json_encode($stats), LOCK_EX); - // === ALLOW REQUEST THROUGH - NO BLOCKING === return; ''' # ============================================================================= -# CACHE VALIDATION FUNCTIONS +# PHP TEMPLATES - COMBINED (Bot + Country Rate-Limiting) # ============================================================================= -def generate_and_validate_cache(httpdocs_path: str, geo_region: str, uid: int = None, gid: int = None): - """Generiert und validiert IP-Range Cache.""" - cache_file = os.path.join(httpdocs_path, CACHE_FILE) - region_info = get_geo_region_info(geo_region) - countries = region_info["countries"] - min_expected = MIN_RANGES.get(geo_region, 1000) +COMBINED_SCRIPT_TEMPLATE = ''' check bot rate-limit + * 4. If human AND country_mode AND country NOT unlimited -> check country rate-limit + * 5. Always log (for stats) + */ - php_script = f''' ['timeout' => 30]]); - $content = @file_get_contents($url, false, $ctx); - if ($content !== false) {{ - $lines = explode("\\n", trim($content)); - foreach ($lines as $line) {{ - $line = trim($line); - if (!empty($line) && strpos($line, '/') !== false) {{ - $ranges[] = $line; +$expiry_date = strtotime('{expiry_timestamp}'); +if (time() > $expiry_date) return; + +// === Configuration === +$bot_mode = {bot_mode}; // true oder false +$country_mode = {country_mode}; // true oder false +$bot_rate_limit = {bot_rate_limit}; +$bot_ban_duration = {bot_ban_duration}; +$country_rate_limit = {country_rate_limit}; +$country_ban_duration = {country_ban_duration}; +$unlimited_countries = [{unlimited_countries}]; // z.B. 'de', 'at', 'ch', ... +$window_size = 60; +$cleanup_probability = 100; + +// === Paths === +$log_file = __DIR__ . '/{log_file}'; +$ratelimit_dir = __DIR__ . '/{ratelimit_dir}'; +$bot_bans_dir = $ratelimit_dir . '/bans'; +$bot_counts_dir = $ratelimit_dir . '/counts'; +$country_bans_dir = $ratelimit_dir . '/country_bans'; +$country_counts_dir = $ratelimit_dir . '/country_counts'; +$country_cache_dir = $ratelimit_dir . '/country_cache'; + +$visitor_ip = $_SERVER['REMOTE_ADDR'] ?? ''; +$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? ''; + +// Ensure directories exist +if ($bot_mode) {{ + if (!is_dir($bot_bans_dir)) @mkdir($bot_bans_dir, 0777, true); + if (!is_dir($bot_counts_dir)) @mkdir($bot_counts_dir, 0777, true); +}} +if ($country_mode) {{ + if (!is_dir($country_bans_dir)) @mkdir($country_bans_dir, 0777, true); + if (!is_dir($country_counts_dir)) @mkdir($country_counts_dir, 0777, true); + if (!is_dir($country_cache_dir)) @mkdir($country_cache_dir, 0777, true); +}} + +// === IP-in-CIDR Check Function === +function ip_in_cidr($ip, $cidr) {{ + if (strpos($cidr, '/') === false) return false; + list($subnet, $mask) = explode('/', $cidr); + $ip_long = ip2long($ip); + $subnet_long = ip2long($subnet); + if ($ip_long === false || $subnet_long === false) return false; + $mask_long = -1 << (32 - (int)$mask); + return ($ip_long & $mask_long) === ($subnet_long & $mask_long); +}} + +// === Country Detection === +function get_country_for_ip($ip, $country_cache_dir) {{ + $countries = ['de', 'at', 'ch', 'us', 'gb', 'fr', 'nl', 'it', 'es', 'pl', 'be', 'se', 'no', 'dk', 'fi', + 'ru', 'cn', 'jp', 'kr', 'in', 'br', 'au', 'ca', 'ua', 'cz', 'pt', 'ie', 'gr', 'hu', 'ro', + 'bg', 'hr', 'sk', 'si', 'lt', 'lv', 'ee', 'lu', 'mt', 'cy', 'tr', 'il', 'za', 'mx', 'ar', + 'cl', 'co', 'pe', 've', 'eg', 'ng', 'ke', 'ma', 'tn', 'pk', 'bd', 'vn', 'th', 'my', 'sg', + 'id', 'ph', 'tw', 'hk', 'nz', 'ae', 'sa', 'qa', 'kw', 'bh', 'om', 'ir', 'iq']; + + foreach ($countries as $country) {{ + $cache_file = "$country_cache_dir/$country.ranges"; + $ranges = []; + + if (file_exists($cache_file) && (time() - filemtime($cache_file)) < 86400) {{ + $ranges = @unserialize(@file_get_contents($cache_file)); + }} + + if (empty($ranges) || !is_array($ranges)) {{ + $url = "https://www.ipdeny.com/ipblocks/data/aggregated/$country-aggregated.zone"; + $ctx = stream_context_create(['http' => ['timeout' => 10]]); + $content = @file_get_contents($url, false, $ctx); + if ($content !== false) {{ + $ranges = []; + foreach (explode("\\n", trim($content)) as $line) {{ + $line = trim($line); + if (!empty($line) && strpos($line, '/') !== false) {{ + $ranges[] = $line; + }} + }} + if (count($ranges) > 100) {{ + @file_put_contents($cache_file, serialize($ranges)); + }} + }} + }} + + if (!empty($ranges)) {{ + foreach ($ranges as $range) {{ + if (ip_in_cidr($ip, $range)) {{ + return strtoupper($country); + }} + }} + }} + }} + + return 'XX'; +}} + +// === Bot IP Ranges === +$bot_ip_ranges = [ + {bot_ip_ranges} +]; + +// === Bot Detection Patterns === +$bot_patterns = [ + {bot_patterns} +]; + +$generic_patterns = [{generic_patterns}]; + +// === STEP 1: Detect Bot === +$detected_bot = null; + +if (!empty($visitor_ip)) {{ + foreach ($bot_ip_ranges as $bot_name => $ip_ranges) {{ + foreach ($ip_ranges as $cidr) {{ + if (ip_in_cidr($visitor_ip, $cidr)) {{ + $detected_bot = $bot_name; + break 2; }} }} }} }} -if (count($ranges) >= {min_expected}) {{ - file_put_contents("{cache_file}", serialize($ranges)); - echo "OK:" . count($ranges); -}} else {{ - echo "FAIL:" . count($ranges); + +if ($detected_bot === null && !empty($user_agent)) {{ + foreach ($bot_patterns as $bot_name => $pattern) {{ + if (preg_match($pattern, $user_agent)) {{ + $detected_bot = $bot_name; + break; + }} + }} + + if ($detected_bot === null) {{ + $ua_lower = strtolower($user_agent); + foreach ($generic_patterns as $pattern) {{ + if (strpos($ua_lower, $pattern) !== false) {{ + $detected_bot = "Bot ($pattern)"; + break; + }} + }} + }} }} + +// === STEP 2: Detect Country === +$country = 'XX'; +if (!empty($visitor_ip) && filter_var($visitor_ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {{ + $country = get_country_for_ip($visitor_ip, $country_cache_dir); +}} + +$timestamp = date('Y-m-d H:i:s'); +$uri = $_SERVER['REQUEST_URI'] ?? '/'; +$current_time = time(); + +// === STEP 3: Bot Rate-Limiting (if bot detected AND bot_mode active) === +if ($detected_bot !== null && $bot_mode) {{ + $bot_hash = md5($detected_bot); + $ban_file = "$bot_bans_dir/$bot_hash.ban"; + + // Check if bot is banned + if (file_exists($ban_file)) {{ + $ban_content = @file_get_contents($ban_file); + $ban_parts = explode('|', $ban_content, 2); + $ban_until = (int)$ban_parts[0]; + if ($current_time < $ban_until) {{ + $remaining = $ban_until - $current_time; + @file_put_contents($log_file, "[$timestamp] BLOCKED_BOT: $detected_bot | IP: $visitor_ip | Country: $country | Remaining: {{$remaining}}s\\n", FILE_APPEND | LOCK_EX); + header('HTTP/1.1 403 Forbidden'); + header('Retry-After: ' . $remaining); + exit; + }} + @unlink($ban_file); + }} + + // Rate-limit check + $count_file = "$bot_counts_dir/$bot_hash.count"; + $count = 1; + $window_start = $current_time; + + if (file_exists($count_file)) {{ + $fp = @fopen($count_file, 'c+'); + if ($fp && flock($fp, LOCK_EX)) {{ + $content = fread($fp, 100); + if (!empty($content)) {{ + $parts = explode('|', $content); + if (count($parts) === 2) {{ + $window_start = (int)$parts[0]; + $count = (int)$parts[1]; + if ($current_time - $window_start > $window_size) {{ + $window_start = $current_time; + $count = 1; + }} else {{ + $count++; + }} + }} + }} + ftruncate($fp, 0); + rewind($fp); + fwrite($fp, "$window_start|$count"); + flock($fp, LOCK_UN); + fclose($fp); + }} + }} else {{ + @file_put_contents($count_file, "$window_start|$count", LOCK_EX); + }} + + // Check if limit exceeded + if ($count > $bot_rate_limit) {{ + $ban_until = $current_time + $bot_ban_duration; + @file_put_contents($ban_file, "$ban_until|$detected_bot", LOCK_EX); + $ban_minutes = $bot_ban_duration / 60; + @file_put_contents($log_file, "[$timestamp] BANNED_BOT: $detected_bot | IP: $visitor_ip | Country: $country | Exceeded $bot_rate_limit req/min | Ban: {{$ban_minutes}}m\\n", FILE_APPEND | LOCK_EX); + header('HTTP/1.1 403 Forbidden'); + header('Retry-After: ' . $bot_ban_duration); + exit; + }} + + // Bot under limit - log and allow + @file_put_contents($log_file, "[$timestamp] BOT: $detected_bot | IP: $visitor_ip | Country: $country | Count: $count/$bot_rate_limit | URI: $uri\\n", FILE_APPEND | LOCK_EX); + + // Cleanup (probabilistic) + if (rand(1, $cleanup_probability) === 1) {{ + foreach (glob("$bot_bans_dir/*.ban") as $f) {{ + $ban_content = @file_get_contents($f); + $ban_time = (int)explode('|', $ban_content, 2)[0]; + if ($current_time > $ban_time) @unlink($f); + }} + foreach (glob("$bot_counts_dir/*.count") as $f) {{ + if ($current_time - filemtime($f) > $window_size * 2) @unlink($f); + }} + }} + + return; // Bot handled, don't check country +}} + +// === STEP 4: Country Rate-Limiting (if human AND country_mode active) === +if ($detected_bot === null && $country_mode) {{ + $country_lower = strtolower($country); + + // Check if country is unlimited (still log but don't rate-limit) + if (in_array($country_lower, $unlimited_countries)) {{ + @file_put_contents($log_file, "[$timestamp] HUMAN | IP: $visitor_ip | Country: $country | URI: $uri\\n", FILE_APPEND | LOCK_EX); + return; // Unlimited country - allow through + }} + + // Country needs rate-limiting + $ban_file = "$country_bans_dir/$country_lower.ban"; + + // Check if country is banned + if (file_exists($ban_file)) {{ + $ban_until = (int)@file_get_contents($ban_file); + if ($current_time < $ban_until) {{ + $remaining = $ban_until - $current_time; + @file_put_contents($log_file, "[$timestamp] BLOCKED_COUNTRY: $country | IP: $visitor_ip | Remaining: {{$remaining}}s\\n", FILE_APPEND | LOCK_EX); + header('HTTP/1.1 403 Forbidden'); + header('Retry-After: ' . $remaining); + exit; + }} + @unlink($ban_file); + }} + + // Rate-limit check + $count_file = "$country_counts_dir/$country_lower.count"; + $count = 1; + $window_start = $current_time; + + if (file_exists($count_file)) {{ + $fp = @fopen($count_file, 'c+'); + if ($fp && flock($fp, LOCK_EX)) {{ + $content = fread($fp, 100); + if (!empty($content)) {{ + $parts = explode('|', $content); + if (count($parts) === 2) {{ + $window_start = (int)$parts[0]; + $count = (int)$parts[1]; + if ($current_time - $window_start > $window_size) {{ + $window_start = $current_time; + $count = 1; + }} else {{ + $count++; + }} + }} + }} + ftruncate($fp, 0); + rewind($fp); + fwrite($fp, "$window_start|$count"); + flock($fp, LOCK_UN); + fclose($fp); + }} + }} else {{ + @file_put_contents($count_file, "$window_start|$count", LOCK_EX); + }} + + // Check if limit exceeded + if ($count > $country_rate_limit) {{ + $ban_until = $current_time + $country_ban_duration; + @file_put_contents($ban_file, "$ban_until", LOCK_EX); + $ban_minutes = $country_ban_duration / 60; + @file_put_contents($log_file, "[$timestamp] BANNED_COUNTRY: $country | IP: $visitor_ip | Exceeded $country_rate_limit req/min | Ban: {{$ban_minutes}}m\\n", FILE_APPEND | LOCK_EX); + header('HTTP/1.1 403 Forbidden'); + header('Retry-After: ' . $country_ban_duration); + exit; + }} + + // Human under limit - log and allow + @file_put_contents($log_file, "[$timestamp] HUMAN | IP: $visitor_ip | Country: $country | Count: $count/$country_rate_limit | URI: $uri\\n", FILE_APPEND | LOCK_EX); + + // Cleanup (probabilistic) + if (rand(1, $cleanup_probability) === 1) {{ + foreach (glob("$country_bans_dir/*.ban") as $f) {{ + $ban_time = (int)@file_get_contents($f); + if ($current_time > $ban_time) @unlink($f); + }} + foreach (glob("$country_counts_dir/*.count") as $f) {{ + if ($current_time - filemtime($f) > $window_size * 2) @unlink($f); + }} + }} + + return; +}} + +// === STEP 5: No rate-limiting active, just log === +if ($detected_bot !== null) {{ + @file_put_contents($log_file, "[$timestamp] BOT: $detected_bot | IP: $visitor_ip | Country: $country | URI: $uri\\n", FILE_APPEND | LOCK_EX); +}} else {{ + @file_put_contents($log_file, "[$timestamp] HUMAN | IP: $visitor_ip | Country: $country | URI: $uri\\n", FILE_APPEND | LOCK_EX); +}} + +return; ''' - temp_php = os.path.join(httpdocs_path, '_jtl-wafi_cache_gen.php') - try: - with open(temp_php, 'w') as f: - f.write(php_script) - result = subprocess.run(['php', temp_php], capture_output=True, text=True, timeout=120) - output = result.stdout.strip() - if output.startswith('OK:'): - if os.path.isfile(cache_file): - set_owner(cache_file, uid, gid) - return True, int(output.split(':')[1]), None - elif output.startswith('FAIL:'): - return False, int(output.split(':')[1]), f"Nur {output.split(':')[1]} Ranges (min. {min_expected} erwartet)" - return False, 0, f"Unerwartete Ausgabe: {output}" - except subprocess.TimeoutExpired: - return False, 0, "Timeout beim Laden der IP-Ranges" - except Exception as e: - return False, 0, str(e) - finally: - if os.path.exists(temp_php): - os.remove(temp_php) - - -def validate_existing_cache(httpdocs_path: str, geo_region: str): - """Validiert bestehenden Cache.""" - cache_file = os.path.join(httpdocs_path, CACHE_FILE) - min_expected = MIN_RANGES.get(geo_region, 1000) - - if not os.path.exists(cache_file): - return False, 0, "Cache-Datei existiert nicht" - - php_script = f'''= min_expected: - return True, count, None - return False, count, f"Nur {count} Ranges (min. {min_expected} erwartet)" - return False, 0, "Cache-Datei ist korrupt" - except Exception as e: - return False, 0, str(e) - # ============================================================================= # SHOP REGISTRY FUNCTIONS # ============================================================================= -def add_shop_to_active(shop: str, mode: str = "geoip", geo_region: str = "dach", - rate_limit: int = None, ban_duration: int = None, - bot_monitor_only: bool = False): - """Registriert einen Shop als aktiv.""" +def add_shop_to_active(shop: str, + bot_mode: bool = True, + bot_rate_limit: int = None, + bot_ban_duration: int = None, + country_mode: bool = False, + country_rate_limit: int = None, + country_ban_duration: int = None, + unlimited_countries: List[str] = None, + monitor_only: bool = False): + """ + Registriert einen Shop als aktiv mit v2.5 Konfiguration. + + Args: + shop: Domain des Shops + bot_mode: Bot-Rate-Limiting aktivieren + bot_rate_limit: Requests/min pro Bot-Type + bot_ban_duration: Ban-Dauer in Sekunden fรผr Bots + country_mode: Country-Rate-Limiting aktivieren + country_rate_limit: Requests/min pro Land + country_ban_duration: Ban-Dauer in Sekunden fรผr Lรคnder + unlimited_countries: Liste der Lรคnder ohne Rate-Limit + monitor_only: Nur Monitoring, kein Blocking + """ os.makedirs(os.path.dirname(ACTIVE_SHOPS_FILE), exist_ok=True) shops = {} if os.path.isfile(ACTIVE_SHOPS_FILE): @@ -1296,15 +1700,21 @@ def add_shop_to_active(shop: str, mode: str = "geoip", geo_region: str = "dach", shop_data = { "activated": datetime.now().isoformat(), "expiry": (datetime.now() + timedelta(hours=72)).isoformat(), - "mode": mode, - "geo_region": geo_region + "bot_mode": bot_mode, + "country_mode": country_mode, + "monitor_only": monitor_only } - if rate_limit is not None: - shop_data["rate_limit"] = rate_limit - if ban_duration is not None: - shop_data["ban_duration"] = ban_duration - if bot_monitor_only: - shop_data["bot_monitor_only"] = True + + if bot_rate_limit is not None: + shop_data["bot_rate_limit"] = bot_rate_limit + if bot_ban_duration is not None: + shop_data["bot_ban_duration"] = bot_ban_duration + if country_rate_limit is not None: + shop_data["country_rate_limit"] = country_rate_limit + if country_ban_duration is not None: + shop_data["country_ban_duration"] = country_ban_duration + if unlimited_countries: + shop_data["unlimited_countries"] = unlimited_countries shops[shop] = shop_data with open(ACTIVE_SHOPS_FILE, 'w') as f: @@ -1326,49 +1736,41 @@ def remove_shop_from_active(shop: str): pass -def get_shop_mode(shop: str) -> str: - """Gibt den Modus eines Shops zurรผck.""" +def get_shop_config(shop: str) -> Dict[str, Any]: + """Gibt die komplette Konfiguration eines Shops zurรผck.""" if not os.path.isfile(ACTIVE_SHOPS_FILE): - return "geoip" + return {} try: with open(ACTIVE_SHOPS_FILE, 'r') as f: - return json.load(f).get(shop, {}).get("mode", "geoip") + return json.load(f).get(shop, {}) except: - return "geoip" + return {} -def get_shop_geo_region(shop: str) -> str: - """Gibt die Geo-Region eines Shops zurรผck.""" - if not os.path.isfile(ACTIVE_SHOPS_FILE): - return "dach" - try: - with open(ACTIVE_SHOPS_FILE, 'r') as f: - return json.load(f).get(shop, {}).get("geo_region", "dach") - except: - return "dach" +def get_shop_bot_config(shop: str) -> tuple: + """Gibt Bot-Rate-Limit Config zurรผck (enabled, rate_limit, ban_duration).""" + config = get_shop_config(shop) + return ( + config.get("bot_mode", False), + config.get("bot_rate_limit"), + config.get("bot_ban_duration") + ) -def get_shop_rate_limit_config(shop: str): - """Gibt Rate-Limit Config eines Shops zurรผck.""" - if not os.path.isfile(ACTIVE_SHOPS_FILE): - return None, None - try: - with open(ACTIVE_SHOPS_FILE, 'r') as f: - shop_data = json.load(f).get(shop, {}) - return shop_data.get("rate_limit"), shop_data.get("ban_duration") - except: - return None, None +def get_shop_country_config(shop: str) -> tuple: + """Gibt Country-Rate-Limit Config zurรผck (enabled, rate_limit, ban_duration, unlimited).""" + config = get_shop_config(shop) + return ( + config.get("country_mode", False), + config.get("country_rate_limit"), + config.get("country_ban_duration"), + config.get("unlimited_countries", []) + ) def get_shop_monitor_mode(shop: str) -> bool: """Gibt zurรผck ob ein Shop im Monitor-Only Modus ist.""" - if not os.path.isfile(ACTIVE_SHOPS_FILE): - return False - try: - with open(ACTIVE_SHOPS_FILE, 'r') as f: - return json.load(f).get(shop, {}).get("bot_monitor_only", False) - except: - return False + return get_shop_config(shop).get("monitor_only", False) def get_shop_activation_time(shop: str) -> Optional[datetime]: @@ -1416,20 +1818,29 @@ def get_active_shops() -> List[str]: # ============================================================================= # ACTIVATE / DEACTIVATE BLOCKING # ============================================================================= -def activate_blocking(shop: str, silent: bool = True, mode: str = "geoip", - geo_region: str = "dach", rate_limit: int = None, - ban_duration: int = None, bot_monitor_only: bool = False) -> bool: +def activate_blocking(shop: str, silent: bool = True, + bot_mode: bool = True, + bot_rate_limit: int = None, + bot_ban_duration: int = None, + country_mode: bool = False, + country_rate_limit: int = None, + country_ban_duration: int = None, + unlimited_countries: List[str] = None, + monitor_only: bool = False) -> bool: """ - Aktiviert Blocking fรผr einen Shop. + Aktiviert Blocking fรผr einen Shop (v2.5). Args: shop: Domain des Shops silent: Keine Konsolenausgabe - mode: "geoip" oder "bot" - geo_region: "dach", "eurozone" oder "none" - rate_limit: Requests pro Minute (nur bei bot-mode) - ban_duration: Ban-Dauer in Sekunden (nur bei bot-mode) - bot_monitor_only: Nur Monitoring, kein Blocking (nur bei bot-mode) + bot_mode: Bot-Rate-Limiting aktivieren + bot_rate_limit: Requests/min pro Bot-Type + bot_ban_duration: Ban-Dauer in Sekunden fรผr Bots + country_mode: Country-Rate-Limiting aktivieren + country_rate_limit: Requests/min pro Land + country_ban_duration: Ban-Dauer in Sekunden fรผr Lรคnder + unlimited_countries: Liste der Lรคnder ohne Rate-Limit + monitor_only: Nur Monitoring, kein Blocking Returns: True wenn erfolgreich, False sonst @@ -1440,15 +1851,17 @@ def activate_blocking(shop: str, silent: bool = True, mode: str = "geoip", blocking_file = os.path.join(httpdocs, BLOCKING_FILE) ratelimit_path = os.path.join(httpdocs, RATELIMIT_DIR) - bot_mode = is_bot_mode(mode) - - if bot_mode: - region_info = get_geo_region_info("none") - geo_region = "none" - else: - region_info = get_geo_region_info(geo_region) - - min_ranges = MIN_RANGES.get(geo_region, 1000) + # Defaults setzen + if bot_rate_limit is None: + bot_rate_limit = DEFAULT_RATE_LIMIT + if bot_ban_duration is None: + bot_ban_duration = DEFAULT_BAN_DURATION * 60 + if country_rate_limit is None: + country_rate_limit = DEFAULT_COUNTRY_RATE_LIMIT + if country_ban_duration is None: + country_ban_duration = DEFAULT_COUNTRY_BAN_DURATION + if unlimited_countries is None: + unlimited_countries = [] # Prรผfe ob bereits aktiv if os.path.isfile(backup_php): @@ -1464,8 +1877,19 @@ def activate_blocking(shop: str, silent: bool = True, mode: str = "geoip", # Ermittle Owner uid, gid = get_most_common_owner(httpdocs) + # Log-Meldung erstellen + mode_parts = [] + if monitor_only: + mode_parts.append("MONITOR") + else: + if bot_mode: + mode_parts.append(f"Bot({bot_rate_limit}/min)") + if country_mode: + mode_parts.append(f"Country({country_rate_limit}/min, {len(unlimited_countries)} unlimited)") + mode_str = " + ".join(mode_parts) if mode_parts else "None" + if not silent: - logger.info(f"Aktiviere {region_info['icon']} {region_info['name']} fรผr: {shop}") + logger.info(f"Aktiviere {shop}: {mode_str}") try: # Step 1: Backup und PHP-Blocking aktivieren @@ -1493,82 +1917,68 @@ def activate_blocking(shop: str, silent: bool = True, mode: str = "geoip", expiry = datetime.now() + timedelta(hours=72) - # Step 2: Blocking-Script erstellen - if bot_mode: - # Verzeichnisse erstellen (fรผr beide Modi) - os.makedirs(ratelimit_path, mode=0o777, exist_ok=True) - os.chmod(ratelimit_path, 0o777) - set_owner(ratelimit_path, uid, gid, recursive=True) + # Step 2: Verzeichnisstruktur erstellen + os.makedirs(ratelimit_path, mode=0o777, exist_ok=True) + os.chmod(ratelimit_path, 0o777) - if bot_monitor_only: - # Monitor-Only Modus: Nur Logging, kein Blocking - geoip_content = BOT_MONITOR_TEMPLATE.format( - expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'), - expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'), - log_file=SHOP_LOG_FILE, - ratelimit_dir=RATELIMIT_DIR, - bot_patterns=generate_php_bot_patterns(), - generic_patterns=generate_php_generic_patterns(), - bot_ip_ranges=generate_php_bot_ip_ranges() - ) - if not silent: - logger.info(f"๐Ÿ” Monitor-Only Modus fรผr {shop}") - else: - # Rate-Limit Modus: Bans/Counts Verzeichnisse erstellen - os.makedirs(os.path.join(ratelimit_path, 'bans'), mode=0o777, exist_ok=True) - os.makedirs(os.path.join(ratelimit_path, 'counts'), mode=0o777, exist_ok=True) - os.chmod(os.path.join(ratelimit_path, 'bans'), 0o777) - os.chmod(os.path.join(ratelimit_path, 'counts'), 0o777) - set_owner(ratelimit_path, uid, gid, recursive=True) - - if rate_limit is None: - rate_limit = DEFAULT_RATE_LIMIT - if ban_duration is None: - ban_duration = DEFAULT_BAN_DURATION * 60 - - geoip_content = BOT_SCRIPT_TEMPLATE.format( - expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'), - expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'), - log_file=SHOP_LOG_FILE, - ratelimit_dir=RATELIMIT_DIR, - bot_patterns=generate_php_bot_patterns(), - generic_patterns=generate_php_generic_patterns(), - bot_ip_ranges=generate_php_bot_ip_ranges(), - rate_limit=rate_limit, - ban_duration=ban_duration, - ban_duration_min=ban_duration // 60 - ) - else: - countries_array = generate_php_countries_array(geo_region) - geoip_content = GEOIP_SCRIPT_TEMPLATE.format( - region_name=region_info['name'], - region_description=region_info['description'], + # Step 3: Blocking-Script erstellen + if monitor_only: + # Monitor-Only Modus: Nur Logging, kein Blocking + script_content = MONITOR_TEMPLATE.format( expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'), expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'), - cache_file=CACHE_FILE, log_file=SHOP_LOG_FILE, - countries_array=countries_array, - min_ranges=min_ranges + ratelimit_dir=RATELIMIT_DIR, + bot_patterns=generate_php_bot_patterns(), + generic_patterns=generate_php_generic_patterns(), + bot_ip_ranges=generate_php_bot_ip_ranges() + ) + if not silent: + logger.info(f"Monitor-Only Modus aktiviert") + else: + # Combined Script (Bot + Country Rate-Limiting) + script_content = COMBINED_SCRIPT_TEMPLATE.format( + expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'), + expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'), + log_file=SHOP_LOG_FILE, + ratelimit_dir=RATELIMIT_DIR, + bot_patterns=generate_php_bot_patterns(), + generic_patterns=generate_php_generic_patterns(), + bot_ip_ranges=generate_php_bot_ip_ranges(), + bot_mode='true' if bot_mode else 'false', + bot_mode_enabled='Enabled' if bot_mode else 'Disabled', + bot_rate_limit=bot_rate_limit, + bot_ban_duration=bot_ban_duration, + bot_ban_duration_min=bot_ban_duration // 60, + country_mode='true' if country_mode else 'false', + country_mode_enabled='Enabled' if country_mode else 'Disabled', + country_rate_limit=country_rate_limit, + country_ban_duration=country_ban_duration, + country_ban_duration_min=country_ban_duration // 60, + unlimited_countries=generate_php_unlimited_countries(unlimited_countries), + unlimited_countries_list=', '.join(unlimited_countries) if unlimited_countries else 'None' ) with open(blocking_file, 'w', encoding='utf-8') as f: - f.write(geoip_content) + f.write(script_content) set_owner(blocking_file, uid, gid) - - # Step 3: Cache generieren (nur bei JTL-WAFi) - if not bot_mode: - success, range_count, error = generate_and_validate_cache(httpdocs, geo_region, uid, gid) - if not success and not silent: - logger.warning(f"Cache-Generierung: {error}") + set_owner(ratelimit_path, uid, gid, recursive=True) # Step 4: Registrieren - if bot_mode: - add_shop_to_active(shop, mode, geo_region, rate_limit, ban_duration, bot_monitor_only) - else: - add_shop_to_active(shop, mode, geo_region) + add_shop_to_active( + shop=shop, + bot_mode=bot_mode, + bot_rate_limit=bot_rate_limit, + bot_ban_duration=bot_ban_duration, + country_mode=country_mode, + country_rate_limit=country_rate_limit, + country_ban_duration=country_ban_duration, + unlimited_countries=unlimited_countries, + monitor_only=monitor_only + ) if not silent: - logger.info(f"โœ… Blocking aktiviert fรผr {shop}") + logger.info(f"Blocking aktiviert fรผr {shop}") return True @@ -1641,11 +2051,12 @@ def deactivate_blocking(shop: str, silent: bool = True) -> bool: # ============================================================================= def get_shop_log_stats(shop: str) -> Dict[str, Any]: """ - Sammelt Statistiken aus dem Shop-Log. + Sammelt Statistiken aus dem Shop-Log (v2.5). Returns: - Dict mit log_entries, total_bans, active_bans, banned_bots, - req_per_min, unique_ips, unique_bots, top_bots, top_ips + Dict mit log_entries, bot_bans, country_bans, active_bot_bans, + active_country_bans, banned_bots, banned_countries, req_per_min, + unique_ips, unique_bots, unique_countries, top_bots, top_ips, top_countries """ httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs') log_file = os.path.join(httpdocs, SHOP_LOG_FILE) @@ -1653,18 +2064,26 @@ def get_shop_log_stats(shop: str) -> Dict[str, Any]: stats = { 'log_entries': 0, - 'total_bans': 0, - 'active_bans': 0, + 'bot_bans': 0, + 'country_bans': 0, + 'active_bot_bans': 0, + 'active_country_bans': 0, 'banned_bots': [], + 'banned_countries': [], 'req_per_min': 0.0, 'unique_ips': 0, 'unique_bots': 0, + 'unique_countries': 0, 'top_bots': {}, - 'top_ips': {} + 'top_ips': {}, + 'top_countries': {}, + 'human_requests': 0, + 'bot_requests': 0 } ips = {} bots = {} + countries = {} # Log-Datei auswerten if os.path.isfile(log_file): @@ -1672,58 +2091,74 @@ def get_shop_log_stats(shop: str) -> Dict[str, Any]: with open(log_file, 'r') as f: for line in f: stats['log_entries'] += 1 - ip, ua = None, 'Unknown' + ip = None detected_bot = None + country = None - if 'BANNED: ' in line: - stats['total_bans'] += 1 + # Bot-Bans erkennen + if 'BANNED_BOT: ' in line or 'BANNED: ' in line: + stats['bot_bans'] += 1 try: - detected_bot = line.split('BANNED: ')[1].split(' |')[0].strip() - except: - pass - elif 'BOT: ' in line: - try: - detected_bot = line.split('BOT: ')[1].split(' |')[0].strip() - except: - pass - elif 'MONITOR: ' in line: - # Monitor-Only Mode - gleiche Statistik wie BOT: - try: - detected_bot = line.split('MONITOR: ')[1].split(' |')[0].strip() - except: - pass - elif 'BLOCKED (banned): ' in line: - try: - detected_bot = line.split('BLOCKED (banned): ')[1].split(' |')[0].strip() + if 'BANNED_BOT: ' in line: + detected_bot = line.split('BANNED_BOT: ')[1].split(' |')[0].strip() + else: + detected_bot = line.split('BANNED: ')[1].split(' |')[0].strip() except: pass + # Country-Bans erkennen + elif 'BANNED_COUNTRY: ' in line: + stats['country_bans'] += 1 + try: + country = line.split('BANNED_COUNTRY: ')[1].split(' |')[0].strip() + except: + pass + + # Bot-Requests erkennen + elif 'BOT: ' in line or 'BLOCKED_BOT: ' in line or 'MONITOR_BOT: ' in line: + stats['bot_requests'] += 1 + try: + if 'BOT: ' in line: + detected_bot = line.split('BOT: ')[1].split(' |')[0].strip() + elif 'BLOCKED_BOT: ' in line: + detected_bot = line.split('BLOCKED_BOT: ')[1].split(' |')[0].strip() + elif 'MONITOR_BOT: ' in line: + detected_bot = line.split('MONITOR_BOT: ')[1].split(' |')[0].strip() + except: + pass + + # Human-Requests erkennen + elif 'HUMAN' in line or 'BLOCKED_COUNTRY: ' in line or 'MONITOR_HUMAN' in line: + stats['human_requests'] += 1 + + # IP extrahieren if 'IP: ' in line: try: ip = line.split('IP: ')[1].split(' |')[0].strip() except: pass - if 'UA: ' in line: + + # Country extrahieren + if 'Country: ' in line: try: - ua = line.split('UA: ')[1].split(' |')[0].strip() + country = line.split('Country: ')[1].split(' |')[0].strip() except: pass + # Statistiken sammeln if ip: - if ip not in ips: - ips[ip] = {'count': 0, 'ua': ua} - ips[ip]['count'] += 1 + ips[ip] = ips.get(ip, 0) + 1 if detected_bot: bots[detected_bot] = bots.get(detected_bot, 0) + 1 - elif ua and ua != 'Unknown': - bot_name = detect_bot(ua, ip) - if bot_name != 'Unbekannt': - bots[bot_name] = bots.get(bot_name, 0) + 1 + + if country and country != 'XX': + countries[country] = countries.get(country, 0) + 1 + except: pass - # Aktive Bans zรคhlen + # Aktive Bot-Bans zรคhlen bans_dir = os.path.join(ratelimit_path, 'bans') if os.path.isdir(bans_dir): now = time.time() @@ -1737,24 +2172,48 @@ def get_shop_log_stats(shop: str) -> Dict[str, Any]: ban_until = int(parts[0]) bot_name = parts[1] if len(parts) > 1 else "Unbekannt" if now < ban_until: - stats['active_bans'] += 1 + stats['active_bot_bans'] += 1 stats['banned_bots'].append(bot_name) except: pass except: pass + # Aktive Country-Bans zรคhlen + country_bans_dir = os.path.join(ratelimit_path, 'country_bans') + if os.path.isdir(country_bans_dir): + now = time.time() + try: + for ban_file in os.listdir(country_bans_dir): + if ban_file.endswith('.ban'): + try: + with open(os.path.join(country_bans_dir, ban_file), 'r') as f: + ban_until = int(f.read().strip()) + if now < ban_until: + stats['active_country_bans'] += 1 + country_code = ban_file.replace('.ban', '').upper() + stats['banned_countries'].append(country_code) + except: + pass + except: + pass + # Statistiken berechnen stats['unique_ips'] = len(ips) stats['unique_bots'] = len(bots) + stats['unique_countries'] = len(countries) # Top Bots (max 10) sorted_bots = sorted(bots.items(), key=lambda x: x[1], reverse=True)[:10] stats['top_bots'] = dict(sorted_bots) # Top IPs (max 10) - sorted_ips = sorted(ips.items(), key=lambda x: x[1]['count'], reverse=True)[:10] - stats['top_ips'] = {ip: data['count'] for ip, data in sorted_ips} + sorted_ips = sorted(ips.items(), key=lambda x: x[1], reverse=True)[:10] + stats['top_ips'] = dict(sorted_ips) + + # Top Countries (max 10) + sorted_countries = sorted(countries.items(), key=lambda x: x[1], reverse=True)[:10] + stats['top_countries'] = dict(sorted_countries) # Req/min berechnen activation_time = get_shop_activation_time(shop) @@ -2006,7 +2465,7 @@ class JTLWAFiAgent: return {"total": len(available), "active": len(active)} def _get_all_shops_data(self) -> List[Dict[str, Any]]: - """Sammelt Daten aller Shops.""" + """Sammelt Daten aller Shops (v2.5).""" shops_data = [] available = get_available_shops() active = get_active_shops() @@ -2023,17 +2482,18 @@ class JTLWAFiAgent: } if is_active: - shop_mode = get_shop_mode(shop) - shop_geo = get_shop_geo_region(shop) - rate_limit, ban_duration = get_shop_rate_limit_config(shop) + config = get_shop_config(shop) activation_time = get_shop_activation_time(shop) - monitor_only = get_shop_monitor_mode(shop) - shop_data["mode"] = shop_mode - shop_data["geo_region"] = shop_geo - shop_data["rate_limit"] = rate_limit - shop_data["ban_duration"] = ban_duration - shop_data["bot_monitor_only"] = monitor_only + # v2.5 Konfiguration + shop_data["bot_mode"] = config.get("bot_mode", False) + shop_data["bot_rate_limit"] = config.get("bot_rate_limit") + shop_data["bot_ban_duration"] = config.get("bot_ban_duration") + shop_data["country_mode"] = config.get("country_mode", False) + shop_data["country_rate_limit"] = config.get("country_rate_limit") + shop_data["country_ban_duration"] = config.get("country_ban_duration") + shop_data["unlimited_countries"] = config.get("unlimited_countries", []) + shop_data["monitor_only"] = config.get("monitor_only", False) if activation_time: shop_data["activated"] = activation_time.isoformat() @@ -2177,41 +2637,51 @@ class JTLWAFiAgent: logger.error(f"Fehler bei Nachrichtenverarbeitung: {e}") async def _handle_activate_command(self, data: Dict[str, Any]): - """Verarbeitet activate-Command.""" + """Verarbeitet activate-Command (v2.5).""" command_id = data.get('command_id', 'unknown') shop = data.get('shop') - mode = data.get('mode', 'geoip') - geo_region = data.get('geo_region', 'dach') - rate_limit = data.get('rate_limit') - ban_duration = data.get('ban_duration') - bot_monitor_only = data.get('bot_monitor_only', False) - # Korrektes Logging je nach Modus - if mode == 'bot': - if bot_monitor_only: - logger.info(f"Aktiviere {shop} (mode=bot, MONITOR-ONLY)") - else: - logger.info(f"Aktiviere {shop} (mode=bot, rate_limit={rate_limit}/min, ban={ban_duration}s)") + # v2.5 Parameter + bot_mode = data.get('bot_mode', True) + bot_rate_limit = data.get('bot_rate_limit') + bot_ban_duration = data.get('bot_ban_duration') + country_mode = data.get('country_mode', False) + country_rate_limit = data.get('country_rate_limit') + country_ban_duration = data.get('country_ban_duration') + unlimited_countries = data.get('unlimited_countries', []) + monitor_only = data.get('monitor_only', False) + + # Log-Meldung erstellen + mode_parts = [] + if monitor_only: + mode_parts.append("MONITOR") else: - logger.info(f"Aktiviere {shop} (mode=geoip, region={geo_region})") + if bot_mode: + mode_parts.append(f"Bot({bot_rate_limit or 'default'}/min)") + if country_mode: + mode_parts.append(f"Country({country_rate_limit or 'default'}/min)") + mode_str = " + ".join(mode_parts) if mode_parts else "None" + logger.info(f"Aktiviere {shop}: {mode_str}") try: success = activate_blocking( - shop, + shop=shop, silent=True, - mode=mode, - geo_region=geo_region, - rate_limit=rate_limit, - ban_duration=ban_duration, - bot_monitor_only=bot_monitor_only + bot_mode=bot_mode, + bot_rate_limit=bot_rate_limit, + bot_ban_duration=bot_ban_duration, + country_mode=country_mode, + country_rate_limit=country_rate_limit, + country_ban_duration=country_ban_duration, + unlimited_countries=unlimited_countries, + monitor_only=monitor_only ) if success: - mode_desc = 'monitor' if bot_monitor_only else mode await self._send_event('command.result', { 'command_id': command_id, 'status': 'success', - 'message': f'Shop {shop} aktiviert ({mode_desc})', + 'message': f'Shop {shop} aktiviert ({mode_str})', 'shop': shop }) # Full Update senden