fix: debounce WHO on JOIN to prevent flood on netsplit recovery
WHO doesn't support multiple targets (absent from TARGMAX on all major IRCds). Replace per-nick WHO with a debounced per-channel WHO: on JOIN, schedule WHO #channel after 2s delay. Subsequent JOINs within the window reset the timer, so a netsplit producing dozens of JOINs results in a single WHO. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -293,12 +293,12 @@ class TestAdmin:
|
||||
assert any("Opers:" in r for r in replies)
|
||||
|
||||
def test_oper_detection_on_join(self):
|
||||
"""User joining a channel triggers WHO; oper detected mid-session."""
|
||||
"""User joining a channel triggers debounced WHO for oper detection."""
|
||||
h = _Harness(channels=["#test"])
|
||||
h.inject_registration()
|
||||
# User joins the channel after the bot is already connected
|
||||
# User joins after the bot is connected
|
||||
h.conn.inject(":oper!oper@operhost JOIN #test")
|
||||
# Server responds to the WHO triggered by the JOIN
|
||||
# Server responds to the debounced WHO #channel
|
||||
h.conn.inject(
|
||||
":server 352 test #test oper operhost irc.server oper H* "
|
||||
":0 Oper Name"
|
||||
@@ -306,13 +306,28 @@ class TestAdmin:
|
||||
h.privmsg("oper", "#test", "!admins", user="oper", host="operhost")
|
||||
asyncio.run(h.run())
|
||||
|
||||
# Verify WHO was sent for the joining nick
|
||||
who_sent = [s for s in h.conn.sent if "WHO" in s and "oper" in s]
|
||||
# Verify debounced WHO was sent for the channel (not the nick)
|
||||
who_sent = [s for s in h.conn.sent if "WHO" in s and "#test" in s]
|
||||
assert len(who_sent) >= 1
|
||||
# Oper detected from the 352 reply
|
||||
replies = h.sent_privmsgs("#test")
|
||||
assert not any("Permission denied" in r for r in replies)
|
||||
assert any("oper!oper@operhost" in r for r in replies)
|
||||
|
||||
def test_join_who_debounce(self):
|
||||
"""Multiple rapid JOINs produce only one WHO per channel."""
|
||||
h = _Harness(channels=["#test"])
|
||||
h.inject_registration()
|
||||
# Simulate netsplit recovery: many users rejoin at once
|
||||
for nick in ("alice", "bob", "carol", "dave", "eve"):
|
||||
h.conn.inject(f":{nick}!{nick}@host JOIN #test")
|
||||
asyncio.run(h.run())
|
||||
|
||||
# Only the initial connect WHO + one debounced WHO, not 5
|
||||
who_lines = [s for s in h.conn.sent
|
||||
if "WHO" in s and "#test" in s]
|
||||
assert len(who_lines) == 2 # 001 connect + 1 debounced
|
||||
|
||||
|
||||
# -- Channel filter ---------------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user