162 lines
5.5 KiB
Python
162 lines
5.5 KiB
Python
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)
|