Fix JOIN confirmation tracking for auto-rejoin

This commit is contained in:
3nd3r
2025-12-28 23:11:21 -06:00
parent 5efe1e70bf
commit 51c212e8cd

View File

@@ -92,6 +92,15 @@ class DuckHuntBot:
else: else:
return default return default
return value return value
def _channel_key(self, channel: str) -> str:
"""Normalize channel for internal comparisons (IRC channels are case-insensitive)."""
if not isinstance(channel, str):
return ""
channel = channel.strip()
if channel.startswith('#') or channel.startswith('&'):
return channel.lower()
return channel
def is_admin(self, user): def is_admin(self, user):
if '!' not in user: if '!' not in user:
@@ -466,29 +475,66 @@ class DuckHuntBot:
for channel in channels: for channel in channels:
try: try:
self.send_raw(f"JOIN {channel}") self.send_raw(f"JOIN {channel}")
self.channels_joined.add(channel) # Wait for server JOIN confirmation before marking joined.
if not hasattr(self, 'pending_joins') or not isinstance(self.pending_joins, dict):
self.pending_joins = {}
self.pending_joins[self._channel_key(channel)] = None
except Exception as e: except Exception as e:
self.logger.error(f"Error joining channel {channel}: {e}") self.logger.error(f"Error joining channel {channel}: {e}")
# JOIN failures (numeric replies)
elif command in {"403", "405", "437", "471", "473", "474", "475", "477", "438", "439"}:
# Common formats:
# 471 <me> <#chan> :Cannot join channel (+l)
# 474 <me> <#chan> :Cannot join channel (+b)
# 477 <me> <#chan> :You need to be identified...
our_nick = self.get_config('connection.nick', 'DuckHunt') or 'DuckHunt'
if params and len(params) >= 2 and params[0].lower() == our_nick.lower():
failed_channel = params[1]
reason = trailing or "Join rejected"
failed_key = self._channel_key(failed_channel)
self.channels_joined.discard(failed_key)
if hasattr(self, 'pending_joins') and isinstance(self.pending_joins, dict):
self.pending_joins.pop(failed_key, None)
self.logger.warning(f"Failed to join {failed_channel}: ({command}) {reason}")
return
elif command == "JOIN": elif command == "JOIN":
if len(params) >= 1 and prefix: if prefix:
channel = params[0] # Some servers send either:
# :nick!user@host JOIN #chan
# or
# :nick!user@host JOIN :#chan
channel = None
if len(params) >= 1:
channel = params[0]
elif trailing and isinstance(trailing, str) and trailing.startswith('#'):
channel = trailing
if not channel:
return
channel_key = self._channel_key(channel)
joiner_nick = prefix.split('!')[0] if '!' in prefix else prefix joiner_nick = prefix.split('!')[0] if '!' in prefix else prefix
our_nick = self.get_config('connection.nick', 'DuckHunt') or 'DuckHunt' our_nick = self.get_config('connection.nick', 'DuckHunt') or 'DuckHunt'
# Check if we successfully joined (or rejoined) a channel # Check if we successfully joined (or rejoined) a channel
if joiner_nick and joiner_nick.lower() == our_nick.lower(): if joiner_nick and joiner_nick.lower() == our_nick.lower():
self.channels_joined.add(channel) self.channels_joined.add(channel_key)
self.logger.info(f"Successfully joined channel {channel}") self.logger.info(f"Successfully joined channel {channel}")
# Clear pending join marker
if hasattr(self, 'pending_joins') and isinstance(self.pending_joins, dict):
self.pending_joins.pop(channel_key, None)
# Cancel any pending rejoin attempts for this channel # Cancel any pending rejoin attempts for this channel
if channel in self.rejoin_tasks: if channel_key in self.rejoin_tasks:
self.rejoin_tasks[channel].cancel() self.rejoin_tasks[channel_key].cancel()
del self.rejoin_tasks[channel] del self.rejoin_tasks[channel_key]
# Reset rejoin attempts counter # Reset rejoin attempts counter
if channel in self.rejoin_attempts: if channel_key in self.rejoin_attempts:
self.rejoin_attempts[channel] = 0 self.rejoin_attempts[channel_key] = 0
elif command == "PRIVMSG": elif command == "PRIVMSG":
if len(params) >= 1: if len(params) >= 1:
@@ -509,11 +555,12 @@ class DuckHuntBot:
self.logger.warning(f"Kicked from {channel} by {kicker}: {reason}") self.logger.warning(f"Kicked from {channel} by {kicker}: {reason}")
# Remove from joined channels # Remove from joined channels
self.channels_joined.discard(channel) channel_key = self._channel_key(channel)
self.channels_joined.discard(channel_key)
# Schedule rejoin if auto-rejoin is enabled # Schedule rejoin if auto-rejoin is enabled
if self.get_config('connection.auto_rejoin.enabled', True): if self.get_config('connection.auto_rejoin.enabled', True):
asyncio.create_task(self.schedule_rejoin(channel)) asyncio.create_task(self.schedule_rejoin(channel_key))
elif command == "PING": elif command == "PING":
try: try: