from __future__ import annotations from dataclasses import dataclass, field from typing import TYPE_CHECKING from yuxi.channel.capabilities.levels import ( CapabilityLevel, EditSupport, InteractiveSupport, MarkdownSupport, MediaSupport, StreamingSupport, ThreadSupport, ) if TYPE_CHECKING: from yuxi.channel.plugins.protocol import ChannelCapability __all__ = [ "CapabilityLevel", "CapabilityMatrix", "EditSupport", "InteractiveSupport", "MarkdownSupport", "MediaSupport", "StreamingSupport", "ThreadSupport", "from_channel_capability", ] @dataclass class CapabilityMatrix: text: bool = True markdown: MarkdownSupport = MarkdownSupport.NONE media: set[MediaSupport] = field(default_factory=lambda: {MediaSupport.NONE}) interactive: InteractiveSupport = InteractiveSupport.NONE reactions: bool = False threads: ThreadSupport = ThreadSupport.NONE edits: EditSupport = EditSupport.NONE streaming: StreamingSupport = StreamingSupport.NONE batch_send: bool = False qr_login: bool = False scan_pairing: bool = False pin: bool = False directory: bool = False @staticmethod def _parse_level( value: str, enum_cls: type ) -> MediaSupport | MarkdownSupport | InteractiveSupport | ThreadSupport | EditSupport | StreamingSupport: return enum_cls(value.lower()) def supports(self, capability: str, config: dict | None = None) -> bool: overrides = (config or {}).get("capability_overrides", {}) override = overrides.get(capability) if override == "disabled": return False match capability: case "text" | "reactions" | "batch_send" | "qr_login" | "scan_pairing" | "pin" | "directory": if override == "enabled": return True return bool(getattr(self, capability, False)) case "markdown": effective = self.markdown if override is not None: try: effective = self._parse_level(override, MarkdownSupport) except ValueError: return False return effective != MarkdownSupport.NONE case "media": if override is not None: try: effective = {self._parse_level(override, MediaSupport)} except ValueError: return False else: effective = self.media return bool(effective) and not (len(effective) == 1 and MediaSupport.NONE in effective) case "interactive": effective = self.interactive if override is not None: try: effective = self._parse_level(override, InteractiveSupport) except ValueError: return False return effective != InteractiveSupport.NONE case "threads": effective = self.threads if override is not None: try: effective = self._parse_level(override, ThreadSupport) except ValueError: return False return effective != ThreadSupport.NONE case "edits": effective = self.edits if override is not None: try: effective = self._parse_level(override, EditSupport) except ValueError: return False return effective != EditSupport.NONE case "streaming": effective = self.streaming if override is not None: try: effective = self._parse_level(override, StreamingSupport) except ValueError: return False return effective != StreamingSupport.NONE case _: return False @classmethod def from_channel_capability(cls, capability: ChannelCapability) -> CapabilityMatrix: from yuxi.channel.plugins.protocol import ChannelCapability as CC matrix = cls() if capability & CC.TEXT: matrix.text = True if capability & CC.MARKDOWN: matrix.markdown = MarkdownSupport.BASIC if capability & (CC.IMAGE | CC.FILE | CC.AUDIO | CC.VIDEO): media: set[MediaSupport] = set() if capability & CC.IMAGE: media.add(MediaSupport.IMAGE) if capability & CC.FILE: media.add(MediaSupport.FILE) if capability & CC.AUDIO: media.add(MediaSupport.AUDIO) if capability & CC.VIDEO: media.add(MediaSupport.VIDEO) matrix.media = media or {MediaSupport.NONE} if capability & CC.INTERACTIVE: matrix.interactive = InteractiveSupport.STATIC if capability & CC.REACTION: matrix.reactions = True if capability & CC.THREAD: matrix.threads = ThreadSupport.REPLY if capability & CC.EDIT: matrix.edits = EditSupport.WITHIN_MINUTE if capability & CC.STREAMING: matrix.streaming = StreamingSupport.TYPING return matrix def from_channel_capability(capability: ChannelCapability) -> CapabilityMatrix: return CapabilityMatrix.from_channel_capability(capability)