ForcePilot/backend/package/yuxi/channel/capabilities/__init__.py
Kris bab30f2715
Some checks failed
Deploy VitePress site to Pages / build (push) Has been cancelled
Ruff Format Check / Ruff Format & Lint (push) Has been cancelled
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
feat:0715
2026-07-15 12:30:58 +08:00

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)