From e5e04b41300d00a892463811a175004a351089f4 Mon Sep 17 00:00:00 2001 From: Jean Date: Thu, 16 Jul 2026 10:16:48 +0000 Subject: [PATCH] engine: reintroduce a killed service agent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A KILL of a services pseudo-client only reintroduced BotServ bots; a killed core agent (NickServ, ChanServ, …) fell through to the forget-user path and stayed off the network until a services restart. UserKilled now recognises an agent by its fixed uid and re-emits its introduction, so an oper can't KILL services down. --- src/engine/mod.rs | 11 +++++++++++ src/engine/tests.rs | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/engine/mod.rs b/src/engine/mod.rs index e374746..089eed3 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -1044,6 +1044,17 @@ impl Engine { Vec::new() } NetEvent::UserKilled { uid } => { + // A killed service agent (NickServ, ChanServ, …) has a fixed uid; + // bring it straight back so an oper can't KILL it off the network. + if let Some(intro) = self.services.iter().find(|s| s.uid() == uid).map(|s| NetAction::IntroduceUser { + uid: s.uid().to_string(), + nick: s.nick().to_string(), + ident: "services".to_string(), + host: s.host().to_string(), + gecos: s.gecos().to_string(), + }) { + return vec![intro]; + } // If the ircd killed one of our bots, forget it so reconcile // reintroduces it (and rejoins its channels); a killed real user is // simply gone. diff --git a/src/engine/tests.rs b/src/engine/tests.rs index 7e66e54..dcec81e 100644 --- a/src/engine/tests.rs +++ b/src/engine/tests.rs @@ -1923,6 +1923,16 @@ assert!(out.iter().any(|a| matches!(a, NetAction::ServiceJoin { channel, .. } if channel == "#c")), "reintroduced bot rejoins #c: {out:?}"); } + // Killing a service agent (NickServ etc.) reintroduces it — an oper can't + // take services off the network with a KILL. + #[test] + fn killed_service_agent_is_reintroduced() { + let mut e = engine_with("killagent", "alice", "sesame"); + // engine_with wires NickServ at uid 42SAAAAAA. + let out = e.handle(NetEvent::UserKilled { uid: "42SAAAAAA".into() }); + assert!(out.iter().any(|a| matches!(a, NetAction::IntroduceUser { uid, nick, .. } if uid == "42SAAAAAA" && !nick.is_empty())), "killed agent reintroduced: {out:?}"); + } + // A channel that expires with an assigned bot parts the bot in the same // sweep — it must not linger in a channel it no longer serves. #[test]