Switch nick to clod, identify to NickServ, allow #/g/tv
- Nick is now clod with clod_ as fallback; while on a fallback nick the server retries NICK clod every nick_retry_seconds (default 180). - Identify to NickServ whenever holding the configured nick, using the password in nickserv_password.txt (gitignored). - Add #/g/tv to allowed_channels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c50f770f77
commit
fc973790fb
@@ -5,7 +5,8 @@ messages, and exposes tools to join/leave/send/read/list users.
|
||||
|
||||
It never sends chat text on its own: the only things it writes to the server
|
||||
unprompted are protocol traffic (NICK/USER registration, PONG replies,
|
||||
keepalive PINGs, and re-joining channels you already joined after a reconnect).
|
||||
keepalive PINGs, NickServ IDENTIFY, periodic NICK attempts to reclaim the
|
||||
configured nick, and re-joining channels you already joined after a reconnect).
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
@@ -46,9 +47,24 @@ def load_config() -> dict:
|
||||
cfg.setdefault("realname", cfg["nick"])
|
||||
cfg.setdefault("allowed_channels", [])
|
||||
cfg.setdefault("buffer_size", 200)
|
||||
cfg.setdefault("nickserv_password_file", "")
|
||||
cfg.setdefault("nick_retry_seconds", 180)
|
||||
return cfg
|
||||
|
||||
|
||||
def nickserv_password(cfg: dict) -> str:
|
||||
"""Read the NickServ password from its (gitignored) file, relative to the config."""
|
||||
name = cfg["nickserv_password_file"]
|
||||
if not name:
|
||||
return ""
|
||||
path = CONFIG_PATH.parent / name
|
||||
try:
|
||||
return path.read_text(encoding="utf-8").strip()
|
||||
except FileNotFoundError:
|
||||
log.warning("NickServ password file %s not found; not identifying", path)
|
||||
return ""
|
||||
|
||||
|
||||
def irc_lower(s: str) -> str:
|
||||
"""RFC 1459 case folding, used for channel and nick comparison."""
|
||||
return s.lower().translate(str.maketrans("[]\\~", "{}|^"))
|
||||
@@ -164,6 +180,13 @@ class IRCClient:
|
||||
await self.send_raw(f"NICK {self.nick}")
|
||||
await self.send_raw(f"USER {cfg['username']} 0 * :{cfg['realname']}")
|
||||
|
||||
reclaim = asyncio.create_task(self._reclaim_nick())
|
||||
try:
|
||||
await self._read_loop(reader)
|
||||
finally:
|
||||
reclaim.cancel()
|
||||
|
||||
async def _read_loop(self, reader: asyncio.StreamReader):
|
||||
awaiting_pong = False
|
||||
while True:
|
||||
try:
|
||||
@@ -181,6 +204,24 @@ class IRCClient:
|
||||
if line:
|
||||
await self._handle(line)
|
||||
|
||||
async def _reclaim_nick(self):
|
||||
"""While on a fallback nick, periodically try to switch back to the configured one."""
|
||||
while True:
|
||||
cfg = load_config()
|
||||
await asyncio.sleep(max(30, int(cfg["nick_retry_seconds"])))
|
||||
cfg = load_config()
|
||||
if self.registered.is_set() and not self._is_me(cfg["nick"]):
|
||||
log.info("trying to reclaim nick %s", cfg["nick"])
|
||||
await self.send_raw(f"NICK {cfg['nick']}")
|
||||
|
||||
async def _identify(self):
|
||||
"""Identify to NickServ, but only while holding the configured (registered) nick."""
|
||||
cfg = load_config()
|
||||
password = nickserv_password(cfg)
|
||||
if password and self._is_me(cfg["nick"]):
|
||||
log.info("identifying to NickServ as %s", self.nick)
|
||||
await self.send_raw(f"PRIVMSG NickServ :IDENTIFY {password}")
|
||||
|
||||
async def _quit(self):
|
||||
if self.writer:
|
||||
try:
|
||||
@@ -220,6 +261,7 @@ class IRCClient:
|
||||
self.registered.set()
|
||||
self.status = f"connected as {self.nick}"
|
||||
log.info(self.status)
|
||||
await self._identify()
|
||||
for ch in list(self.wanted):
|
||||
await self.send_raw(f"JOIN {self.display.get(ch, ch)}")
|
||||
elif cmd in ("432", "433", "436") and not self.registered.is_set():
|
||||
@@ -267,6 +309,8 @@ class IRCClient:
|
||||
elif cmd == "NICK" and p:
|
||||
if self._is_me(src):
|
||||
self.nick = p[0]
|
||||
self.status = f"connected as {self.nick}"
|
||||
await self._identify()
|
||||
for ch, nicks in self.joined.items():
|
||||
if src in nicks:
|
||||
nicks.discard(src)
|
||||
|
||||
Reference in New Issue
Block a user