From 54218d2677fc76c3c5c878e3e09be997d0838a4d Mon Sep 17 00:00:00 2001 From: user Date: Fri, 20 Feb 2026 20:01:20 +0100 Subject: [PATCH] fix: suppress connection noise, MOTD, CTCP, and DCC from clients Filter out messages that are useless to bouncer clients: - Server notices (prefix without !, NOTICE to */AUTH) - MOTD numerics (375, 372, 376, 422) - Welcome/stats numerics (001-005, 042, 250-255, 265-266) - User mode changes (MODE to non-channel targets) - CTCP queries and DCC requests (PRIVMSG with \x01, except ACTION) - CTCP replies in NOTICE Filter applies to both live dispatch and backlog replay. Purged existing noise from the backlog database. Co-Authored-By: Claude Opus 4.6 --- src/bouncer/router.py | 58 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/bouncer/router.py b/src/bouncer/router.py index e46680f..44974d4 100644 --- a/src/bouncer/router.py +++ b/src/bouncer/router.py @@ -25,6 +25,59 @@ _TARGET0_COMMANDS = { "PRIVMSG", "NOTICE", "JOIN", "PART", "MODE", "TOPIC", "NAMES", "WHO", "WHOIS", } +# Numerics suppressed from client delivery (connection noise, MOTD, server stats) +_SUPPRESS_NUMERICS = { + # Welcome block (we synthesize our own) + "001", "002", "003", "004", "005", + # Unique ID + "042", + # LUSERS / server stats + "250", "251", "252", "253", "254", "255", + # Local/global user counts + "265", "266", + # MOTD + "375", "372", "376", "422", + # Visible host (handled internally by network) + "396", + # Nick in use (handled internally by network) + "433", +} + +_CTCP_MARKER = "\x01" + + +def _suppress(msg: IRCMessage) -> bool: + """Return True if this message should not reach the client.""" + # Noisy numerics + if msg.command in _SUPPRESS_NUMERICS: + return True + + # Server notices: prefix without '!' is a server, not a user + if msg.command == "NOTICE" and msg.prefix and "!" not in msg.prefix: + return True + + # Connection notices (NOTICE to * or AUTH, regardless of prefix) + if msg.command == "NOTICE" and msg.params and msg.params[0] in ("*", "AUTH"): + return True + + # CTCP replies in NOTICE + if msg.command == "NOTICE" and len(msg.params) >= 2: + if msg.params[1].startswith(_CTCP_MARKER): + return True + + # CTCP/DCC inside PRIVMSG (keep ACTION) + if msg.command == "PRIVMSG" and len(msg.params) >= 2: + text = msg.params[1] + if text.startswith(_CTCP_MARKER) and not text.startswith("\x01ACTION"): + return True + + # User mode changes (MODE for non-channel targets) + if msg.command == "MODE" and msg.params: + if not msg.params[0].startswith(("#", "&", "+", "!")): + return True + + return False + class Router: """Central message hub linking clients to networks.""" @@ -165,6 +218,9 @@ class Router: async def _dispatch(self, network_name: str, msg: IRCMessage) -> None: """Dispatch a network message to attached clients and backlog.""" + if _suppress(msg): + return + # Store in backlog (raw, un-namespaced) if msg.command in BACKLOG_COMMANDS and msg.params: target = msg.params[0] @@ -204,6 +260,8 @@ class Router: params=[entry.target, entry.content], prefix=entry.sender, ) + if _suppress(msg): + continue namespaced = encode_message( msg, network_name, own_nicks, client_nick=client.nick, )