diff --git a/app.py b/app.py index 4e4d563..8d447ea 100644 --- a/app.py +++ b/app.py @@ -39,6 +39,9 @@ class Config: self.fail_open = self._parse_bool("FAIL_OPEN", True) self.debug = self._parse_bool("DEBUG", False) + + self.bot_webhook_url = os.getenv("BOT_WEBHOOK_URL", "") + self.bot_webhook_secret = os.getenv("BOT_WEBHOOK_SECRET", "") def validate(self): return [k for k in self.REQUIRED_KEYS if not os.getenv(k)] @@ -95,6 +98,7 @@ if missing: KNOWN_EXTERNAL_USERS = {} RATE_LIMIT = defaultdict(list) METRICS = defaultdict(int) +DM_NOTIFY = defaultdict(list) METRICS_LOCK = Lock() CACHE_LOCK = Lock() @@ -146,7 +150,10 @@ def periodic_cache_save(): def extract_domain(user_id): try: - return user_id.split(":")[1].lower().rstrip(".") + parts = user_id.split(":") + if len(parts) < 2: + return "unknown" + return parts[1].lower().rstrip(".") except Exception: return "unknown" @@ -180,6 +187,30 @@ def is_local_room(room_id): def get_role(user_id): return "admin" if user_id in config.admin_users else "user" + +def notify_bot(event_type, sender, room_id): + if not config.bot_webhook_url: + return + + try: + headers = {} + + if config.bot_webhook_secret: + headers["Authorization"] = f"Bearer {config.bot_webhook_secret}" + + requests.post( + config.bot_webhook_url, + json={ + "type": event_type, + "sender": sender, + "room_id": room_id, + "timestamp": time.time() + }, + headers=headers, + timeout=2 + ) + except Exception as e: + logger.error(f"Failed to notify bot: {e}") # ============================================================ # RATE LIMIT @@ -430,6 +461,11 @@ def invite(room_id, event_id): # 🔒 Rate Limit if is_rate_limited(domain, sender): + log_event( + "rate_limited", + actor=sender, + domain=domain + ) return Response(status=429) # 🟢 Whitelist @@ -466,17 +502,29 @@ def invite(room_id, event_id): payload ) - else: - with METRICS_LOCK: - METRICS["invite_blocked"] += 1 + key = f"{sender}:{room_id}" + last_list = RATE_LIMIT.get(f"dm_notify:{key}", []) + last = last_list[-1] if last_list else 0 - log_event( - "invite_blocked", - actor=sender, - domain=domain, - reason="unknown_external_user" - ) - return Response(status=403) + # 🔥 deduplicated notify + if time.time() - last > 5: + notify_bot("dm_spam", sender, room_id) + DM_NOTIFY[key].append(time.time()) + + # 🔥 heavy detection unabhängig davon + if is_rate_limited(domain, sender): + notify_bot("dm_spam_heavy", sender, room_id) + + with METRICS_LOCK: + METRICS["dm_detected"] += 1 + + log_event( + "dm_detected", + actor=sender, + domain=domain, + room_id=room_id + ) + # 🟢 DEFAULT (alles andere erlauben) remember_user(sender) diff --git a/env b/env index 39bf98a..466bcc0 100644 --- a/env +++ b/env @@ -2,6 +2,8 @@ TUWUNEL_URL=http://tuwunel:6167 LOCAL_DOMAIN=ztfr.eu ADMIN_TOKEN=4h1bYSgYxfrotpjoXEzLO8LFyXKudqUA +BOT_WEBHOOK_URL=http://maubot:29316/_matrix/maubot/plugin/acb/webhook/dm_detected +BOT_WEBHOOK_SECRET=supersecret123 DOMAIN_WHITELIST=techniverse.net,daddelwerk.net @@ -9,4 +11,4 @@ BLOCK_EXTERNAL_DMS=true ALLOW_ROOM_CREATION=false CACHE_TTL_SECONDS=604800 -DEBUG=true \ No newline at end of file +DEBUG=true