2021-07-06 22:08:22 +08:00
|
|
|
import json
|
2021-07-07 22:36:08 +08:00
|
|
|
import itertools
|
2021-07-04 14:19:36 +08:00
|
|
|
|
2021-07-06 18:01:41 +08:00
|
|
|
from dataclasses import dataclass
|
2021-07-08 10:49:02 +08:00
|
|
|
from typing import Any, Dict, List, Tuple, Type, Union, Mapping, Iterable
|
2021-07-03 13:56:47 +08:00
|
|
|
|
2021-07-01 07:59:50 +08:00
|
|
|
from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment
|
2021-07-03 13:56:47 +08:00
|
|
|
from nonebot.typing import overrides
|
2021-07-01 07:59:50 +08:00
|
|
|
|
|
|
|
|
2021-07-04 14:19:36 +08:00
|
|
|
class MessageSegment(BaseMessageSegment["Message"]):
|
2021-07-03 13:56:47 +08:00
|
|
|
"""
|
|
|
|
飞书 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。
|
|
|
|
"""
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@overrides(BaseMessageSegment)
|
|
|
|
def get_message_class(cls) -> Type["Message"]:
|
|
|
|
return Message
|
2021-07-01 07:59:50 +08:00
|
|
|
|
|
|
|
def __str__(self) -> str:
|
2021-07-08 10:07:55 +08:00
|
|
|
if self.type == "text" or self.type == "hongbao":
|
2021-07-03 13:56:47 +08:00
|
|
|
return str(self.data["text"])
|
2021-07-07 22:36:08 +08:00
|
|
|
|
2021-07-08 10:07:55 +08:00
|
|
|
elif self.type == "image":
|
2021-07-07 22:36:08 +08:00
|
|
|
return "[图片]"
|
|
|
|
|
2021-07-03 13:56:47 +08:00
|
|
|
return ""
|
2021-07-01 07:59:50 +08:00
|
|
|
|
2021-07-04 14:19:36 +08:00
|
|
|
@overrides(BaseMessageSegment)
|
2021-07-01 07:59:50 +08:00
|
|
|
def __add__(self, other) -> "Message":
|
2021-07-04 14:19:36 +08:00
|
|
|
return Message(self) + (MessageSegment.text(other) if isinstance(
|
|
|
|
other, str) else other)
|
2021-07-01 07:59:50 +08:00
|
|
|
|
2021-07-04 14:19:36 +08:00
|
|
|
@overrides(BaseMessageSegment)
|
2021-07-01 07:59:50 +08:00
|
|
|
def __radd__(self, other) -> "Message":
|
2021-07-04 14:19:36 +08:00
|
|
|
return (MessageSegment.text(other)
|
|
|
|
if isinstance(other, str) else Message(other)) + self
|
2021-07-01 07:59:50 +08:00
|
|
|
|
2021-07-04 14:19:36 +08:00
|
|
|
@overrides(BaseMessageSegment)
|
2021-07-01 07:59:50 +08:00
|
|
|
def is_text(self) -> bool:
|
2021-07-03 13:56:47 +08:00
|
|
|
return self.type == "text"
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def text(text: str) -> "MessageSegment":
|
|
|
|
return MessageSegment("text", {"text": text})
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def post(title: str, content: list) -> "MessageSegment":
|
|
|
|
return MessageSegment("post", {"title": title, "content": content})
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def image(image_key: str) -> "MessageSegment":
|
|
|
|
return MessageSegment("image", {"image_key": image_key})
|
|
|
|
|
|
|
|
@staticmethod
|
2021-07-08 10:07:55 +08:00
|
|
|
def interactive(title: str, elements: list) -> "MessageSegment":
|
|
|
|
return MessageSegment("interactive", {
|
|
|
|
"title": title,
|
|
|
|
"elements": elements
|
2021-07-03 13:56:47 +08:00
|
|
|
})
|
|
|
|
|
2021-07-08 10:07:55 +08:00
|
|
|
@staticmethod
|
|
|
|
def share_chat(chat_id: str) -> "MessageSegment":
|
|
|
|
return MessageSegment("share_chat", {"chat_id": chat_id})
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def share_user(user_id: str) -> "MessageSegment":
|
|
|
|
return MessageSegment("share_user", {"user_id": user_id})
|
|
|
|
|
2021-07-03 13:56:47 +08:00
|
|
|
@staticmethod
|
|
|
|
def audio(file_key: str, duration: int) -> "MessageSegment":
|
|
|
|
return MessageSegment("audio", {
|
|
|
|
"file_key": file_key,
|
|
|
|
"duration": duration
|
|
|
|
})
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def media(file_key: str, image_key: str, file_name: str,
|
|
|
|
duration: int) -> "MessageSegment":
|
|
|
|
return MessageSegment(
|
|
|
|
"media", {
|
|
|
|
"file_key": file_key,
|
|
|
|
"image_key": image_key,
|
|
|
|
"file_name": file_name,
|
|
|
|
"duration": duration
|
|
|
|
})
|
|
|
|
|
|
|
|
@staticmethod
|
2021-07-08 10:07:55 +08:00
|
|
|
def file(file_key: str, file_name: str) -> "MessageSegment":
|
|
|
|
return MessageSegment("file", {
|
|
|
|
"file_key": file_key,
|
|
|
|
"file_name": file_name
|
2021-07-03 13:56:47 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
@staticmethod
|
2021-07-08 10:07:55 +08:00
|
|
|
def sticker(file_key) -> "MessageSegment":
|
|
|
|
return MessageSegment("sticker", {"file_key": file_key})
|
2021-07-01 07:59:50 +08:00
|
|
|
|
|
|
|
|
2021-07-04 14:19:36 +08:00
|
|
|
class Message(BaseMessage[MessageSegment]):
|
2021-07-03 13:56:47 +08:00
|
|
|
"""
|
|
|
|
飞书 协议 Message 适配。
|
|
|
|
"""
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@overrides(BaseMessage)
|
|
|
|
def get_segment_class(cls) -> Type[MessageSegment]:
|
|
|
|
return MessageSegment
|
2021-07-01 07:59:50 +08:00
|
|
|
|
2021-07-04 14:19:36 +08:00
|
|
|
@overrides(BaseMessage)
|
|
|
|
def __add__(self, other: Union[str, Mapping,
|
|
|
|
Iterable[Mapping]]) -> "Message":
|
|
|
|
return super(Message, self).__add__(
|
|
|
|
MessageSegment.text(other) if isinstance(other, str) else other)
|
|
|
|
|
|
|
|
@overrides(BaseMessage)
|
|
|
|
def __radd__(self, other: Union[str, Mapping,
|
|
|
|
Iterable[Mapping]]) -> "Message":
|
|
|
|
return super(Message, self).__radd__(
|
|
|
|
MessageSegment.text(other) if isinstance(other, str) else other)
|
|
|
|
|
2021-07-01 07:59:50 +08:00
|
|
|
@staticmethod
|
2021-07-04 14:19:36 +08:00
|
|
|
@overrides(BaseMessage)
|
2021-07-01 07:59:50 +08:00
|
|
|
def _construct(
|
|
|
|
msg: Union[str, Mapping,
|
|
|
|
Iterable[Mapping]]) -> Iterable[MessageSegment]:
|
2021-07-03 13:56:47 +08:00
|
|
|
if isinstance(msg, Mapping):
|
2021-07-06 18:01:41 +08:00
|
|
|
yield MessageSegment(msg["type"], msg.get("data") or {})
|
|
|
|
return
|
2021-07-03 13:56:47 +08:00
|
|
|
elif isinstance(msg, str):
|
|
|
|
yield MessageSegment.text(msg)
|
|
|
|
elif isinstance(msg, Iterable):
|
|
|
|
for seg in msg:
|
2021-07-06 18:01:41 +08:00
|
|
|
if isinstance(seg, MessageSegment):
|
|
|
|
yield seg
|
|
|
|
else:
|
|
|
|
yield MessageSegment(seg["type"], seg.get("data") or {})
|
2021-07-03 13:56:47 +08:00
|
|
|
|
2021-07-08 10:49:02 +08:00
|
|
|
def _merge(self) -> "Message":
|
|
|
|
i: int
|
|
|
|
seg: MessageSegment
|
|
|
|
msg: List[MessageSegment] = []
|
|
|
|
for i, seg in enumerate(self):
|
|
|
|
if seg.type == "text" and i != 0 and msg[-1].type == "text":
|
|
|
|
msg[-1] = MessageSegment(
|
|
|
|
"text", {"text": msg[-1].data["text"] + seg.data["text"]})
|
|
|
|
else:
|
|
|
|
msg.append(seg)
|
|
|
|
return Message(msg)
|
|
|
|
|
2021-07-04 14:19:36 +08:00
|
|
|
@overrides(BaseMessage)
|
|
|
|
def extract_plain_text(self) -> str:
|
|
|
|
return "".join(seg.data["text"] for seg in self if seg.is_text())
|
2021-07-06 18:01:41 +08:00
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class MessageSerializer:
|
|
|
|
"""
|
|
|
|
飞书 协议 Message 序列化器。
|
|
|
|
"""
|
|
|
|
message: Message
|
|
|
|
|
2021-07-06 21:12:49 +08:00
|
|
|
def serialize(self) -> Tuple[str, str]:
|
|
|
|
return self.message[0].type, json.dumps(self.message[0].data)
|
2021-07-06 18:01:41 +08:00
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class MessageDeserializer:
|
|
|
|
"""
|
|
|
|
飞书 协议 Message 反序列化器。
|
|
|
|
"""
|
|
|
|
type: str
|
2021-07-06 21:12:49 +08:00
|
|
|
data: Dict[str, Any]
|
2021-07-06 18:01:41 +08:00
|
|
|
|
2021-07-06 21:12:49 +08:00
|
|
|
def deserialize(self) -> Message:
|
2021-07-08 10:07:55 +08:00
|
|
|
if self.type == "post":
|
|
|
|
msg = Message()
|
|
|
|
if self.data["title"] != "":
|
|
|
|
msg += MessageSegment("text", {'text': self.data["title"]})
|
|
|
|
for seg in itertools.chain(*self.data["content"]):
|
|
|
|
tag = seg.pop("tag")
|
|
|
|
msg += MessageSegment(tag if tag != "img" else "image", seg)
|
2021-07-08 10:49:02 +08:00
|
|
|
return msg._merge()
|
2021-07-08 10:07:55 +08:00
|
|
|
|
|
|
|
else:
|
|
|
|
return Message(MessageSegment(self.type, self.data))
|