2020-07-05 20:39:34 +08:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2020-07-18 18:18:43 +08:00
|
|
|
import abc
|
2020-08-13 15:23:04 +08:00
|
|
|
from functools import reduce, partial
|
2020-08-10 13:06:02 +08:00
|
|
|
from dataclasses import dataclass, field
|
2020-07-11 17:32:03 +08:00
|
|
|
|
|
|
|
from nonebot.config import Config
|
2020-08-14 17:41:24 +08:00
|
|
|
from nonebot.typing import Driver, Message, WebSocket
|
2020-08-13 15:23:04 +08:00
|
|
|
from nonebot.typing import Any, Dict, Union, Optional, Callable, Iterable, Awaitable
|
2020-07-11 17:32:03 +08:00
|
|
|
|
2020-07-05 20:39:34 +08:00
|
|
|
|
2020-07-18 18:18:43 +08:00
|
|
|
class BaseBot(abc.ABC):
|
2020-07-05 20:39:34 +08:00
|
|
|
|
2020-07-18 18:18:43 +08:00
|
|
|
@abc.abstractmethod
|
2020-08-01 22:03:40 +08:00
|
|
|
def __init__(self,
|
2020-08-13 15:23:04 +08:00
|
|
|
driver: Driver,
|
2020-08-07 17:51:57 +08:00
|
|
|
connection_type: str,
|
2020-08-01 22:03:40 +08:00
|
|
|
config: Config,
|
2020-08-13 15:23:04 +08:00
|
|
|
self_id: str,
|
2020-08-01 22:03:40 +08:00
|
|
|
*,
|
2020-08-10 13:06:02 +08:00
|
|
|
websocket: WebSocket = None):
|
2020-08-13 15:23:04 +08:00
|
|
|
self.driver = driver
|
2020-08-07 17:51:57 +08:00
|
|
|
self.connection_type = connection_type
|
|
|
|
self.config = config
|
|
|
|
self.self_id = self_id
|
|
|
|
self.websocket = websocket
|
|
|
|
|
2020-08-13 15:23:04 +08:00
|
|
|
def __getattr__(self, name: str) -> Callable[..., Awaitable[Any]]:
|
|
|
|
return partial(self.call_api, name)
|
|
|
|
|
2020-08-07 17:51:57 +08:00
|
|
|
@property
|
|
|
|
@abc.abstractmethod
|
|
|
|
def type(self) -> str:
|
2020-07-11 17:32:03 +08:00
|
|
|
raise NotImplementedError
|
|
|
|
|
2020-07-18 18:18:43 +08:00
|
|
|
@abc.abstractmethod
|
2020-07-11 17:32:03 +08:00
|
|
|
async def handle_message(self, message: dict):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2020-07-18 18:18:43 +08:00
|
|
|
@abc.abstractmethod
|
2020-07-11 17:32:03 +08:00
|
|
|
async def call_api(self, api: str, data: dict):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
2020-08-13 15:23:04 +08:00
|
|
|
# TODO: improve event
|
2020-08-10 14:50:12 +08:00
|
|
|
class BaseEvent(abc.ABC):
|
2020-07-11 17:32:03 +08:00
|
|
|
|
2020-08-10 14:50:12 +08:00
|
|
|
def __init__(self, raw_event: dict):
|
|
|
|
self._raw_event = raw_event
|
|
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
# TODO: pretty print
|
|
|
|
return f"<Event: >"
|
|
|
|
|
|
|
|
@property
|
2020-08-08 23:08:01 +08:00
|
|
|
@abc.abstractmethod
|
2020-08-11 16:54:45 +08:00
|
|
|
def type(self) -> str:
|
2020-07-11 17:32:03 +08:00
|
|
|
raise NotImplementedError
|
|
|
|
|
2020-08-10 14:50:12 +08:00
|
|
|
@type.setter
|
2020-08-08 23:08:01 +08:00
|
|
|
@abc.abstractmethod
|
2020-08-11 16:54:45 +08:00
|
|
|
def type(self, value) -> None:
|
2020-08-08 23:08:01 +08:00
|
|
|
raise NotImplementedError
|
2020-07-15 20:39:59 +08:00
|
|
|
|
2020-08-10 14:50:12 +08:00
|
|
|
@property
|
|
|
|
@abc.abstractmethod
|
2020-08-11 16:54:45 +08:00
|
|
|
def detail_type(self) -> str:
|
2020-08-10 14:50:12 +08:00
|
|
|
raise NotImplementedError
|
2020-07-15 20:39:59 +08:00
|
|
|
|
2020-08-10 14:50:12 +08:00
|
|
|
@detail_type.setter
|
|
|
|
@abc.abstractmethod
|
2020-08-11 16:54:45 +08:00
|
|
|
def detail_type(self, value) -> None:
|
2020-08-10 14:50:12 +08:00
|
|
|
raise NotImplementedError
|
2020-08-08 23:08:01 +08:00
|
|
|
|
2020-08-10 14:50:12 +08:00
|
|
|
@property
|
|
|
|
@abc.abstractmethod
|
2020-08-11 16:54:45 +08:00
|
|
|
def sub_type(self) -> Optional[str]:
|
2020-08-10 14:50:12 +08:00
|
|
|
raise NotImplementedError
|
2020-08-08 23:08:01 +08:00
|
|
|
|
2020-08-10 14:50:12 +08:00
|
|
|
@sub_type.setter
|
|
|
|
@abc.abstractmethod
|
2020-08-11 16:54:45 +08:00
|
|
|
def sub_type(self, value) -> None:
|
2020-08-10 14:50:12 +08:00
|
|
|
raise NotImplementedError
|
2020-08-08 23:08:01 +08:00
|
|
|
|
2020-08-17 16:09:41 +08:00
|
|
|
@property
|
|
|
|
@abc.abstractmethod
|
|
|
|
def user_id(self) -> Optional[int]:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
@user_id.setter
|
|
|
|
@abc.abstractmethod
|
|
|
|
def user_id(self, value) -> None:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2020-08-14 17:41:24 +08:00
|
|
|
@property
|
|
|
|
@abc.abstractmethod
|
|
|
|
def message(self) -> Optional[Message]:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
@message.setter
|
|
|
|
@abc.abstractmethod
|
|
|
|
def message(self, value) -> None:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
@property
|
|
|
|
@abc.abstractmethod
|
|
|
|
def raw_message(self) -> Optional[str]:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
@raw_message.setter
|
|
|
|
@abc.abstractmethod
|
|
|
|
def raw_message(self, value) -> None:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2020-08-17 16:09:41 +08:00
|
|
|
@property
|
|
|
|
@abc.abstractmethod
|
|
|
|
def plain_text(self) -> Optional[str]:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
@property
|
|
|
|
@abc.abstractmethod
|
|
|
|
def sender(self) -> Optional[dict]:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
@sender.setter
|
|
|
|
@abc.abstractmethod
|
|
|
|
def sender(self, value) -> None:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2020-07-15 20:39:59 +08:00
|
|
|
|
2020-08-10 14:50:12 +08:00
|
|
|
@dataclass
|
|
|
|
class BaseMessageSegment(abc.ABC):
|
|
|
|
type: str
|
|
|
|
data: Dict[str, str] = field(default_factory=lambda: {})
|
2020-07-15 20:39:59 +08:00
|
|
|
|
2020-08-10 14:50:12 +08:00
|
|
|
@abc.abstractmethod
|
|
|
|
def __str__(self):
|
|
|
|
raise NotImplementedError
|
2020-07-11 17:32:03 +08:00
|
|
|
|
2020-08-10 14:50:12 +08:00
|
|
|
@abc.abstractmethod
|
|
|
|
def __add__(self, other):
|
|
|
|
raise NotImplementedError
|
2020-08-08 23:08:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
class BaseMessage(list, abc.ABC):
|
2020-07-11 17:32:03 +08:00
|
|
|
|
2020-07-18 18:18:43 +08:00
|
|
|
def __init__(self,
|
2020-08-11 10:44:05 +08:00
|
|
|
message: Union[str, dict, list, BaseMessageSegment,
|
|
|
|
"BaseMessage"] = None,
|
2020-07-18 18:18:43 +08:00
|
|
|
*args,
|
|
|
|
**kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
2020-08-11 10:44:05 +08:00
|
|
|
if isinstance(message, (str, dict, list)):
|
2020-07-18 18:18:43 +08:00
|
|
|
self.extend(self._construct(message))
|
|
|
|
elif isinstance(message, BaseMessage):
|
|
|
|
self.extend(message)
|
|
|
|
elif isinstance(message, BaseMessageSegment):
|
|
|
|
self.append(message)
|
2020-07-11 17:32:03 +08:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return ''.join((str(seg) for seg in self))
|
2020-07-18 18:18:43 +08:00
|
|
|
|
|
|
|
@staticmethod
|
2020-08-08 23:08:01 +08:00
|
|
|
@abc.abstractmethod
|
2020-08-11 10:44:05 +08:00
|
|
|
def _construct(msg: Union[str, dict, list]) -> Iterable[BaseMessageSegment]:
|
2020-07-18 18:18:43 +08:00
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def __add__(
|
|
|
|
self, other: Union[str, BaseMessageSegment,
|
|
|
|
"BaseMessage"]) -> "BaseMessage":
|
|
|
|
result = self.__class__(self)
|
|
|
|
if isinstance(other, str):
|
|
|
|
result.extend(self._construct(other))
|
|
|
|
elif isinstance(other, BaseMessageSegment):
|
|
|
|
result.append(other)
|
|
|
|
elif isinstance(other, BaseMessage):
|
|
|
|
result.extend(other)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def __radd__(self, other: Union[str, BaseMessageSegment, "BaseMessage"]):
|
|
|
|
result = self.__class__(other)
|
|
|
|
return result.__add__(self)
|
|
|
|
|
|
|
|
def append(self, obj: Union[str, BaseMessageSegment]) -> "BaseMessage":
|
|
|
|
if isinstance(obj, BaseMessageSegment):
|
|
|
|
if obj.type == "text" and self and self[-1].type == "text":
|
|
|
|
self[-1].data["text"] += obj.data["text"]
|
|
|
|
else:
|
|
|
|
super().append(obj)
|
|
|
|
elif isinstance(obj, str):
|
|
|
|
self.extend(self._construct(obj))
|
|
|
|
else:
|
|
|
|
raise ValueError(f"Unexpected type: {type(obj)} {obj}")
|
|
|
|
return self
|
|
|
|
|
|
|
|
def extend(
|
|
|
|
self, obj: Union["BaseMessage",
|
|
|
|
Iterable[BaseMessageSegment]]) -> "BaseMessage":
|
|
|
|
for segment in obj:
|
|
|
|
self.append(segment)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def reduce(self) -> None:
|
|
|
|
index = 0
|
|
|
|
while index < len(self):
|
|
|
|
if index > 0 and self[
|
|
|
|
index - 1].type == "text" and self[index].type == "text":
|
|
|
|
self[index - 1].data["text"] += self[index].data["text"]
|
|
|
|
del self[index]
|
|
|
|
else:
|
|
|
|
index += 1
|
|
|
|
|
|
|
|
def extract_plain_text(self) -> str:
|
|
|
|
|
|
|
|
def _concat(x: str, y: BaseMessageSegment) -> str:
|
|
|
|
return f"{x} {y.data['text']}" if y.type == "text" else x
|
|
|
|
|
|
|
|
return reduce(_concat, self, "")
|