diff --git a/modules/line_handler/ircv3.py b/modules/line_handler/ircv3.py index ceda7e4b..5b3962f7 100644 --- a/modules/line_handler/ircv3.py +++ b/modules/line_handler/ircv3.py @@ -16,13 +16,58 @@ CAPABILITIES = [ utils.irc.Capability(None, "draft/setname", alias="setname") ] +def _cap_depend_sort(caps, server_caps): + sorted_caps = [] + + caps_copy = {alias: cap.copy() for alias, cap in caps.items()} + + for cap in caps.values(): + if not cap.available(server_caps): + del caps_copy[cap.alias] + + while True: + remove = [] + for alias, cap in caps_copy.items(): + for depend_alias in cap.depends_on: + if not depend_alias in caps_copy: + remove.append(alias) + if remove: + for alias in remove: + del caps_copy[alias] + else: + break + + while caps_copy: + fulfilled = [] + for cap in caps_copy.values(): + remove = [] + for depend_alias in cap.depends_on: + if depend_alias in sorted_caps: + remove.append(depend_alias) + for remove_cap in remove: + cap.depends_on.remove(remove_cap) + + if not cap.depends_on: + fulfilled.append(cap.alias) + for fulfilled_cap in fulfilled: + del caps_copy[fulfilled_cap] + sorted_caps.append(fulfilled_cap) + return [caps[alias] for alias in sorted_caps] + def _cap_match(server, caps): matched_caps = {} blacklist = server.get_setting("blacklisted-caps", []) + + cap_aliases = {} for cap in caps: + if not cap.alias in blacklist: + cap_aliases[cap.alias] = cap + + sorted_caps = _cap_depend_sort(cap_aliases, server.server_capabilities) + + for cap in sorted_caps: available = cap.available(server.server_capabilities) - if (available and not server.has_capability(cap) and - not available in blacklist): + if available and not server.has_capability(cap): matched_caps[available] = cap return matched_caps diff --git a/src/utils/irc/__init__.py b/src/utils/irc/__init__.py index c6dcbf0d..d470bdbe 100644 --- a/src/utils/irc/__init__.py +++ b/src/utils/irc/__init__.py @@ -291,6 +291,7 @@ class Capability(object): depends_on: typing.List[str]=None): self.alias = alias or ratified_name self._caps = set([ratified_name, draft_name]) + self.depends_on = depends_on or [] self._on_ack_callbacks = [ ] # type: typing.List[typing.Callable[[], None]] @@ -304,7 +305,8 @@ class Capability(object): return cap[0] if cap else None def copy(self): - return Capability(*self._caps) + return Capability(*self._caps, alias=self.alias, + depends_on=self.depends_on) def on_ack(self, callback: typing.Callable[[], None]): self._on_ack_callbacks.append(callback)