Skip to content
This repository has been archived by the owner on Feb 20, 2024. It is now read-only.

Commit

Permalink
🔖 v0.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
CMHopeSunshine committed Jun 8, 2023
1 parent 9c414f3 commit 074777f
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 80 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ _✨ 米游社大别野Bot Python SDK ✨_

## 特性

- 基于`FastAPI`,异步、快速、高性能!
- 基于`FastAPI``Pydantic`,异步、快速、高性能!
- 完整的类型注解支持
- 便捷的消息构造和发送方法
- 完整的消息段和API支持
- 丰富的消息段和完整的API支持
- ~~想不出来了~~

## 安装
Expand All @@ -25,7 +26,7 @@ _✨ 米游社大别野Bot Python SDK ✨_

## 快速开始

首先你需要一个米游社大别野的Bot,如果没有请先自行申请,拿到`bot_id``bot_secret`
首先你需要一个[米游社大别野](https://dby.miyoushe.com/chat)的Bot,如果没有请先到机器人开发者社区(别野ID: OpenVilla)申请,取得`bot_id``bot_secret`

```python
from villa import Bot
Expand Down
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
[tool.poetry]
name = "villa"
version = "0.1.0"
version = "0.1.1"
description = "米游社大别野Bot Python SDK。MiHoYo Villa Bot Python SDK."
authors = ["CMHopeSunshine <277073121@qq.com>"]
license = "MIT"
readme = "README.md"
homepage = "https://github.com/CMHopeSunshine/villa-py"
repository = "https://github.com/CMHopeSunshine/villa-py"
documentation = "https://github.com/CMHopeSunshine/villa-py"
keywords = ["mihoyo", "bot", "villa"]

[tool.poetry.dependencies]
python = "^3.8"
Expand Down
49 changes: 30 additions & 19 deletions villa/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,12 +411,14 @@ async def delete_room(self, villa_id: int, room_id: int) -> None:

async def get_room(self, villa_id: int, room_id: int) -> Room:
return Room.parse_obj(
await self._request(
"GET",
"vila/api/bot/platform/getRoom",
villa_id,
json={"room_id": room_id},
)
(
await self._request(
"GET",
"vila/api/bot/platform/getRoom",
villa_id,
json={"room_id": room_id},
)
)["room"]
)

async def get_villa_group_room_list(self, villa_id: int) -> GroupRoom:
Expand Down Expand Up @@ -640,49 +642,55 @@ async def _handle_event(self, event: Event):
)

async def _parse_message_content(self, message: Message) -> MessageContentInfo:
if quote := message["quote", 1]:
if quote := message["quote", 0]:
quote = QuoteInfo(**quote.dict())
message_text = ""
message_offset = 0
entities: List[TextEntity] = []
images: List[Image] = []
mentioned = MentionedInfo(type=MentionType.PART)
for i, seg in enumerate(message.__root__):
for seg in message:
try:
space = " " if i != len(message) - 1 else ""
if isinstance(seg, TextSegment):
message_text += seg.content
message_offset += len(seg.content)
elif isinstance(seg, MentionAllSegment):
message_text += "@全体成员{space}"
message_text += f"@{seg.show_text} "
entities.append(
TextEntity(
offset=message_offset, length=6, entity=MentionedAll()
offset=message_offset,
length=6,
entity=MentionedAll(show_text=seg.show_text),
)
)
message_offset += 6
message_offset += len(f"@{seg.show_text} ")
mentioned.type = MentionType.ALL
elif isinstance(seg, MentionRobotSegment):
bot_name = self.bot_info.template.name if self.bot_info else "Bot"
message_text += f"@{bot_name}{space}"
message_text += f"@{bot_name} "
entities.append(
TextEntity(
offset=message_offset,
length=len(f"@{bot_name}".encode("utf-16")) // 2,
entity=MentionedRobot(bot_id=self.bot_id),
entity=MentionedRobot(
bot_id=self.bot_id, bot_name=bot_name
),
)
)
message_offset += len(f"@{bot_name}") + 1
mentioned.user_id_list.append(self.bot_id)
elif isinstance(seg, MentionUserSegment):
# 需要调用API获取被@的用户的昵称
user = await self.get_member(villa_id=seg.villa_id, uid=seg.user_id)
message_text += f"@{user.basic.nickname}{space}"
message_text += f"@{user.basic.nickname} "
entities.append(
TextEntity(
offset=message_offset,
length=len(f"@{user.basic.nickname}".encode("utf-16")) // 2,
entity=MentionedUser(user_id=str(user.basic.uid)),
entity=MentionedUser(
user_id=str(user.basic.uid),
user_name=user.basic.nickname,
),
)
)
message_offset += len(f"@{user.basic.nickname}") + 1
Expand All @@ -692,26 +700,29 @@ async def _parse_message_content(self, message: Message) -> MessageContentInfo:
room = await self.get_room(
villa_id=seg.villa_id, room_id=seg.room_id
)
message_text += f"#{room.room_name}{space}"
message_text += f"#{room.room_name} "
entities.append(
TextEntity(
offset=message_offset,
length=len(f"#{room.room_name}".encode("utf-16")) // 2,
entity=VillaRoomLink(
villa_id=str(seg.villa_id),
room_id=str(seg.room_id),
room_name=room.room_name,
),
)
)
message_offset += len(f"#{room.room_name} ")
elif isinstance(seg, LinkSegment):
show_text = seg.show_text or seg.url
message_text += show_text + space
message_text += show_text
entities.append(
TextEntity(
offset=message_offset,
length=len(show_text.encode("utf-16")) // 2,
entity=Link(url=seg.url),
entity=Link(
url=seg.url, show_text=seg.show_text or seg.url
),
)
)
message_offset += len(show_text) + 1
Expand Down
89 changes: 75 additions & 14 deletions villa/event.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import json
from enum import IntEnum
from typing import Any, Dict, Type, Union, Optional
from typing import Dict, Type, Union, Optional

from pydantic import Extra, BaseModel, validator
from pydantic import Extra, BaseModel, root_validator

from .store import _bots
from .models import MessageContentInfo
Expand Down Expand Up @@ -81,18 +80,80 @@ class SendMessageEvent(Event):
"""大别野ID"""
bot_id: str
"""机器人ID"""
message: Message
"""事件消息"""

@root_validator(pre=True)
def _(cls, data: dict):
msg = Message()
msg_content_info = data["content"]
if quote := msg_content_info.get("quote"):
msg.append(
MessageSegment.quote(
message_id=quote["quoted_message_id"],
message_send_time=quote["quoted_message_send_time"],
)
)

@validator("content", pre=True)
def _content_str_to_dict(cls, v: Any):
if isinstance(v, str):
return json.loads(v)
return v

@property
def message(self) -> Message:
if not hasattr(self, "_message"):
setattr(self, "_message", Message._parse(self.content, self.villa_id))
return getattr(self, "_message")
content = msg_content_info["content"]
text = content["text"]
entities = content["entities"]
if not entities:
return Message(MessageSegment.text(text))
text = text.encode("utf-16")
last_offset: int = 0
last_length: int = 0
for entity in entities:
end_offset: int = last_offset + last_length
offset: int = entity["offset"]
length: int = entity["length"]
entity_detail = entity["entity"]
if offset != end_offset:
msg.append(
MessageSegment.text(
text[((end_offset + 1) * 2) : ((offset + 1) * 2)].decode(
"utf-16"
)
)
)
entity_text = text[(offset + 1) * 2 : (offset + length + 1) * 2].decode(
"utf-16"
)
if entity_detail["type"] == "mentioned_robot":
entity_detail["bot_name"] = entity_text.lstrip("@")[:-1]
msg.append(
MessageSegment.mention_robot(
entity_detail["bot_id"], entity_detail["bot_name"]
)
)
elif entity_detail["type"] == "mentioned_user":
entity_detail["user_name"] = entity_text.lstrip("@")[:-1]
msg.append(
MessageSegment.mention_user(
int(entity_detail["user_id"]), data["villa_id"]
)
)
elif entity_detail["type"] == "mention_all":
entity_detail["show_text"] = entity_text.lstrip("@")[:-1]
msg.append(MessageSegment.mention_all(entity_detail["show_text"]))
elif entity_detail["type"] == "villa_room_link":
entity_detail["room_name"] = entity_text.lstrip("#")[:-1]
msg.append(
MessageSegment.room_link(
int(entity_detail["villa_id"]),
int(entity_detail["room_id"]),
)
)
else:
entity_detail["show_text"] = entity_text
msg.append(MessageSegment.link(entity_detail["url"], entity_text))
last_offset = offset
last_length = length
end_offset = last_offset + last_length
if last_text := text[(end_offset + 1) * 2 :].decode("utf-16"):
msg.append(MessageSegment.text(last_text))
data["message"] = msg
return data

async def send(
self,
Expand Down
52 changes: 9 additions & 43 deletions villa/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ def text(text: str) -> "Text":
return Text(content=text)

@staticmethod
def mention_robot(bot_id: str, bot_name: Optional[str] = None) -> "MentionRobot":
def mention_robot(bot_id: str, bot_name: str) -> "MentionRobot":
return MentionRobot(bot_id=bot_id, bot_name=bot_name)

@staticmethod
def mention_user(villa_id: int, user_id: int) -> "MentionUser":
return MentionUser(villa_id=villa_id, user_id=user_id)

@staticmethod
def mention_all() -> "MentionAll":
return MentionAll()
def mention_all(show_text: str = "全体成员") -> "MentionAll":
return MentionAll(show_text=show_text)

@staticmethod
def room_link(villa_id: int, room_id: int) -> "RoomLink":
Expand Down Expand Up @@ -99,7 +99,7 @@ class MentionRobot(MessageSegment):

type: Literal["mention_robot"] = "mention_robot"
bot_id: str
bot_name: Optional[str] = None
bot_name: str


class MentionUser(MessageSegment):
Expand All @@ -114,6 +114,7 @@ class MentionAll(MessageSegment):
"""@全体成员消息段"""

type: Literal["mention_all"] = "mention_all"
show_text: str = "全体成员"


class RoomLink(MessageSegment):
Expand Down Expand Up @@ -220,7 +221,7 @@ def mention_all(self) -> Self:
self.__root__.append(MentionAll())
return self

def mention_robot(self, bot_id: str, bot_name: Optional[str] = None) -> Self:
def mention_robot(self, bot_id: str, bot_name: str) -> Self:
"""提及(@at)机器人消息
参数:
Expand Down Expand Up @@ -331,41 +332,6 @@ def append(self, segment: Union[str, MessageSegment]):
segment = Text(content=segment)
self.__root__.append(segment)

@classmethod
def _parse(cls, content: MessageContentInfo, villa_id: int) -> Self:
msg = cls()
text = content.content.text
text_begin = 0
for entity in content.content.entities:
if isinstance(entity.entity, MentionedRobotInfo):
msg.mention_robot(entity.entity.bot_id)
elif isinstance(entity.entity, MentionedUserInfo):
msg.mention_user(villa_id, int(entity.entity.user_id))
elif isinstance(entity.entity, MentionedAllInfo):
msg.mention_all()
elif isinstance(entity.entity, VillaRoomLinkInfo):
msg.room_link(int(entity.entity.villa_id), int(entity.entity.room_id))
elif isinstance(entity.entity, LinkInfo):
msg.link(entity.entity.url, entity.entity.url)
if text_sengment := text[text_begin : entity.offset]:
msg.text(text_sengment)
text = text[(entity.offset + entity.length) :]
if text:
msg.text(text)
if content.content.images:
for image in content.content.images:
msg.image(
image.url,
image.size.width if image.size else None,
image.size.height if image.size else None,
image.file_size,
)
if content.quote:
msg.quote(
content.quote.quoted_message_id, content.quote.quoted_message_send_time
)
return msg

def plain_text(self) -> str:
"""获取纯文本消息内容"""
return "".join(
Expand Down Expand Up @@ -625,9 +591,9 @@ def __getitem__(
if arg2 is None:
return Message([seg for seg in self.__root__ if seg.type == arg1])
elif isinstance(arg2, int):
if l := [seg for seg in self.__root__ if seg.type == arg1]:
return l[arg2]
else:
try:
return [seg for seg in self.__root__ if seg.type == arg1][arg2]
except IndexError:
return None
elif isinstance(arg2, slice):
return Message([seg for seg in self.__root__ if seg.type == arg1][arg2])
Expand Down
Loading

0 comments on commit 074777f

Please sign in to comment.