from __future__ import annotations from yuxi.utils.logging_config import logger class ChannelRuntime: def __init__(self): self._joined_channels: set[str] = set() self._channel_members: dict[str, set[str]] = {} self._channel_modes: dict[str, str] = {} self._channel_topics: dict[str, str] = {} @property def joined_channels(self) -> set[str]: return self._joined_channels def add_channel(self, channel: str) -> None: self._joined_channels.add(channel) if channel not in self._channel_members: self._channel_members[channel] = set() def remove_channel(self, channel: str) -> None: self._joined_channels.discard(channel) def handle_names(self, channel: str, names_str: str) -> None: members = {name.lstrip("@+") for name in names_str.split() if name} self._channel_members[channel] = members logger.debug(f"IRC NAMES {channel}: {len(members)} members") def handle_join(self, channel: str, nick: str) -> None: if channel in self._channel_members: self._channel_members[channel].add(nick) logger.debug(f"IRC JOIN: {nick} -> {channel}") def handle_part(self, channel: str, nick: str) -> None: if channel in self._channel_members: self._channel_members[channel].discard(nick) logger.debug(f"IRC PART: {nick} <- {channel}") def handle_kick(self, channel: str, nick: str) -> None: if channel in self._channel_members: self._channel_members[channel].discard(nick) logger.debug(f"IRC KICK: {nick} from {channel}") def handle_quit(self, nick: str) -> None: for channel in self._channel_members: self._channel_members[channel].discard(nick) logger.debug(f"IRC QUIT: {nick}") def handle_nick_change(self, old_nick: str, new_nick: str) -> None: for channel in self._channel_members: members = self._channel_members[channel] if old_nick in members: members.discard(old_nick) members.add(new_nick) logger.debug(f"IRC NICK: {old_nick} -> {new_nick}") def get_members(self, channel: str) -> set[str]: return self._channel_members.get(channel, set()) def handle_mode(self, channel: str, modes: str) -> None: channel_lower = channel.lower() current = self._channel_modes.get(channel_lower, "") self._channel_modes[channel_lower] = _merge_modes(current, modes) logger.debug(f"IRC MODE {channel}: {modes} -> {self._channel_modes[channel_lower]}") def set_topic(self, channel: str, topic: str) -> None: self._channel_topics[channel.lower()] = topic def get_topic(self, channel: str) -> str | None: return self._channel_topics.get(channel.lower()) def get_modes(self, channel: str) -> str: return self._channel_modes.get(channel.lower(), "") def _merge_modes(current: str, incoming: str) -> str: if not current: return incoming if not incoming: return current adding = True mode_set: set[str] = set() for char in current: if char in "+-": continue mode_set.add(char) for char in incoming: if char == "+": adding = True elif char == "-": adding = False elif adding: mode_set.add(char) else: mode_set.discard(char) sorted_modes = sorted(mode_set) return "+" + "".join(sorted_modes)