Skip to content

Commit

Permalink
✨ 适配 DoDo
Browse files Browse the repository at this point in the history
  • Loading branch information
AzideCupric committed Nov 13, 2023
1 parent cf926e7 commit d69f5ee
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 85 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# ----- Project -----
.env*

# Created by https://www.toptal.com/developers/gitignore/api/python,node,visualstudiocode,jetbrains,macos,windows,linux
# Edit at https://www.toptal.com/developers/gitignore?templates=python,node,visualstudiocode,jetbrains,macos,windows,linux
Expand Down
1 change: 1 addition & 0 deletions nonebot_plugin_saa/adapters/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from . import red as red
from . import dodo as dodo
from . import feishu as feishu
from . import qqguild as qqguild
from . import kaiheila as kaiheila
Expand Down
211 changes: 211 additions & 0 deletions nonebot_plugin_saa/adapters/dodo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
from functools import partial
from contextlib import suppress
from typing import Any, Dict, List, Literal, Optional, cast

from nonebot import logger
from nonebot.adapters import Event
from nonebot.drivers import Request
from nonebot.adapters import Bot as BaseBot

from ..types import Text, Image, Reply, Mention
from ..auto_select_bot import register_list_targets
from ..utils import SupportedAdapters, SupportedPlatform
from ..abstract_factories import (
MessageFactory,
MessageSegmentFactory,
register_ms_adapter,
assamble_message_factory,
)
from ..registries import (
Receipt,
MessageId,
PlatformTarget,
TargetDoDoChannel,
TargetDoDoPrivate,
register_sender,
register_convert_to_arg,
register_target_extractor,
register_message_id_getter,
)

with suppress(ImportError):
from nonebot.adapters.dodo import Bot as BotDodo
from nonebot.adapters.dodo.models import MessageBody
from nonebot.adapters.dodo.message import Message, MessageSegment
from nonebot.adapters.dodo.event import (
MessageEvent,
ChannelMessageEvent,
PersonalMessageEvent,
)

adapter = SupportedAdapters.dodo
register_dodo = partial(register_ms_adapter, adapter)

class DodoMessageId(MessageId):
adapter_name: Literal[adapter] = adapter

message_id: str
reason: Optional[None] = None

@register_message_id_getter(MessageEvent)
def _get_message_id(event: Event) -> DodoMessageId:
assert isinstance(event, MessageEvent)
return DodoMessageId(message_id=event.message_id)

Check warning on line 53 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L52-L53

Added lines #L52 - L53 were not covered by tests

@register_dodo(Text)
def _text(text: Text) -> MessageSegment:
return MessageSegment.text(text.data["text"])

Check warning on line 57 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L57

Added line #L57 was not covered by tests

@register_dodo(Image)
async def _image(image: Image, bot: BaseBot) -> MessageSegment:
if not isinstance(bot, BotDodo):
raise TypeError(f"Unsupported type of bot: {type(bot)}")

Check warning on line 62 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L61-L62

Added lines #L61 - L62 were not covered by tests

file = image.data["image"]
if isinstance(file, str):

Check warning on line 65 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L64-L65

Added lines #L64 - L65 were not covered by tests
# 要求必须是官方链接,因此需要下载一遍
logger.debug(f"Downloading image: {file}")
req = Request("GET", file, timeout=10)
resp = await bot.adapter.request(req)
if resp.status_code != 200:
raise RuntimeError(

Check warning on line 71 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L67-L71

Added lines #L67 - L71 were not covered by tests
f"Failed to download image: {resp.status_code}, url: {file}"
)
file = resp.content
logger.trace(f"Downloaded image: {file}")
logger.debug(

Check warning on line 76 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L74-L76

Added lines #L74 - L76 were not covered by tests
f"Downloaded image type: {type(file)}, size: {len(file or '')}"
)
if not isinstance(file, bytes):
raise TypeError(f"Unsupported type of file: {type(file)}, need bytes")

Check warning on line 80 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L79-L80

Added lines #L79 - L80 were not covered by tests

logger.debug("Uploading image...")
upload_result = await bot.set_resouce_picture_upload(

Check warning on line 83 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L82-L83

Added lines #L82 - L83 were not covered by tests
file=file, file_name=image.data["name"] + ".png" # 上传是文件名必须携带有效后缀
)
logger.debug(f"Uploaded result: {upload_result}")
return MessageSegment.picture(**upload_result.dict())

Check warning on line 87 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L86-L87

Added lines #L86 - L87 were not covered by tests

@register_dodo(Reply)
def _reply(reply: Reply) -> MessageSegment:
assert isinstance(reply.data, DodoMessageId)
return MessageSegment.reference(reply.data.message_id)

Check warning on line 92 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L91-L92

Added lines #L91 - L92 were not covered by tests

@register_dodo(Mention)
def _mention(mention: Mention) -> MessageSegment:
return MessageSegment.at_user(dodo_id=mention.data["user_id"])

Check warning on line 96 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L96

Added line #L96 was not covered by tests

@register_target_extractor(ChannelMessageEvent)
def _extract_channel_msg_event(event: Event) -> TargetDoDoChannel:
assert isinstance(event, ChannelMessageEvent)
return TargetDoDoChannel(channel_id=event.channel_id)

Check warning on line 101 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L100-L101

Added lines #L100 - L101 were not covered by tests

@register_target_extractor(PersonalMessageEvent)
def _extract_personal_msg_event(event: Event) -> TargetDoDoPrivate:
assert isinstance(event, PersonalMessageEvent)
island_source_id = event.island_source_id
if island_source_id is None:
raise ValueError("island_source_id is None")
return TargetDoDoPrivate(

Check warning on line 109 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L105-L109

Added lines #L105 - L109 were not covered by tests
dodo_source_id=event.dodo_source_id, island_source_id=island_source_id
)

@register_convert_to_arg(adapter, SupportedPlatform.dodo_channel)
def _gen_channel(target: PlatformTarget) -> Dict[str, Any]:
assert isinstance(target, TargetDoDoChannel)
return {

Check warning on line 116 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L115-L116

Added lines #L115 - L116 were not covered by tests
"channel_id": target.channel_id,
}

@register_convert_to_arg(adapter, SupportedPlatform.dodo_private)
def _gen_private(target: PlatformTarget) -> Dict[str, Any]:
assert isinstance(target, TargetDoDoPrivate)
return {

Check warning on line 123 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L122-L123

Added lines #L122 - L123 were not covered by tests
"dodo_source_id": target.dodo_source_id,
"island_source_id": target.island_source_id,
}

class DodoReceipt(Receipt):
adapter_name: Literal[adapter] = adapter
message_id: str
reason: Optional[str] = None

async def revoke(self):
return await cast(BotDodo, self._get_bot()).set_channel_message_withdraw(

Check warning on line 134 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L134

Added line #L134 was not covered by tests
message_id=self.message_id, reason=self.reason
)

async def edit(self, mesaage_body: MessageBody):
return await cast(BotDodo, self._get_bot()).set_channel_message_edit(

Check warning on line 139 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L139

Added line #L139 was not covered by tests
message_id=self.message_id, message_body=mesaage_body
)

async def pin(self, is_cancel: bool = False):
"""置顶消息"""
return await cast(BotDodo, self._get_bot()).set_channel_message_top(

Check warning on line 145 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L145

Added line #L145 was not covered by tests
message_id=self.message_id, is_cancel=is_cancel
)

@property
def raw(self) -> str:
return self.message_id

Check warning on line 151 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L151

Added line #L151 was not covered by tests

@register_sender(adapter)
async def send(
bot,
msg: MessageFactory[MessageSegmentFactory],
target,
event,
at_sender: bool,
reply: bool,
) -> DodoReceipt:
assert isinstance(bot, BotDodo)
assert isinstance(target, (TargetDoDoChannel, TargetDoDoPrivate))

Check warning on line 163 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L162-L163

Added lines #L162 - L163 were not covered by tests

if event:
assert isinstance(event, MessageEvent)
full_msg = assamble_message_factory(

Check warning on line 167 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L165-L167

Added lines #L165 - L167 were not covered by tests
msg,
Mention(event.get_user_id()),
Reply(DodoMessageId(message_id=event.message_id)),
at_sender,
reply,
)
else:
full_msg = msg

Check warning on line 175 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L175

Added line #L175 was not covered by tests

message_to_send = Message()
for segment_factory in full_msg:
message_segment = await segment_factory.build(bot)
message_to_send += message_segment

Check warning on line 180 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L177-L180

Added lines #L177 - L180 were not covered by tests

if isinstance(target, TargetDoDoChannel):
if target.dodo_source_id:
resp = await bot.send_to_channel_personal(

Check warning on line 184 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L182-L184

Added lines #L182 - L184 were not covered by tests
message=message_to_send, **target.arg_dict(bot)
)
else:
resp = await bot.send_to_channel(

Check warning on line 188 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L188

Added line #L188 was not covered by tests
message=message_to_send, **target.arg_dict(bot)
)
else:
logger.debug(f"Sending to personal: {target.arg_dict(bot)}")
resp = await bot.send_to_personal(

Check warning on line 193 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L192-L193

Added lines #L192 - L193 were not covered by tests
message=message_to_send, **target.arg_dict(bot)
)

return DodoReceipt(message_id=resp, bot_id=bot.self_id)

Check warning on line 197 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L197

Added line #L197 was not covered by tests

@register_list_targets(adapter)
async def list_targets(bot: BaseBot) -> List[PlatformTarget]:
assert isinstance(bot, BotDodo)
targets = []
for island in await bot.get_island_list():
for channel in await bot.get_channel_list(

Check warning on line 204 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L201-L204

Added lines #L201 - L204 were not covered by tests
island_source_id=island.island_source_id
):
targets.append(TargetDoDoChannel(channel_id=channel.channel_id))

Check warning on line 207 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L207

Added line #L207 was not covered by tests

# TODO: 私聊

return targets

Check warning on line 211 in nonebot_plugin_saa/adapters/dodo.py

View check run for this annotation

Codecov / codecov/patch

nonebot_plugin_saa/adapters/dodo.py#L211

Added line #L211 was not covered by tests
2 changes: 2 additions & 0 deletions nonebot_plugin_saa/registries/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from .platform_send_target import register_sender as register_sender
from .platform_send_target import TargetOB12Unknow as TargetOB12Unknow
from .platform_send_target import QQGuildDMSManager as QQGuildDMSManager
from .platform_send_target import TargetDoDoChannel as TargetDoDoChannel
from .platform_send_target import TargetDoDoPrivate as TargetDoDoPrivate
from .platform_send_target import TargetFeishuGroup as TargetFeishuGroup
from .platform_send_target import TargetFeishuPrivate as TargetFeishuPrivate
from .platform_send_target import TargetQQGuildDirect as TargetQQGuildDirect
Expand Down
30 changes: 30 additions & 0 deletions nonebot_plugin_saa/registries/platform_send_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,36 @@ class TargetFeishuGroup(PlatformTarget):
chat_id: str


class TargetDoDoChannel(PlatformTarget):
"""DoDo Channel
参数
channel_id: 频道ID
dodo_source_id: 用户 ID(可选)
"""

platform_type: Literal[
SupportedPlatform.dodo_channel
] = SupportedPlatform.dodo_channel
channel_id: str
dodo_source_id: Optional[str] = None


class TargetDoDoPrivate(PlatformTarget):
"""DoDo Private
参数
dodo_source_id: 用户 ID
island_source_id: 群 ID
"""

platform_type: Literal[
SupportedPlatform.dodo_private
] = SupportedPlatform.dodo_private
island_source_id: str
dodo_source_id: str


# this union type is for deserialize pydantic model with nested PlatformTarget
AllSupportedPlatformTarget = Union[
TargetQQGroup,
Expand Down
3 changes: 3 additions & 0 deletions nonebot_plugin_saa/utils/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class SupportedAdapters(StrEnum):
telegram = "Telegram"
feishu = "Feishu"
red = "RedProtocol"
dodo = "DoDo"

fake = "fake" # for nonebug

Expand All @@ -25,6 +26,8 @@ class SupportedPlatform(StrEnum):
telegram_forum = "Telegram Forum"
feishu_private = "Feishu Private"
feishu_group = "Feishu Group"
dodo_channel = "DoDo Channel"
dodo_private = "DoDo Private"


supported_adapter_names = set(SupportedAdapters._member_map_.values()) # noqa: SLF001
Loading

0 comments on commit d69f5ee

Please sign in to comment.