Skip to content
This repository was archived by the owner on Oct 21, 2023. It is now read-only.

Commit 9de6b6f

Browse files
feat: QOL
1 parent e79317b commit 9de6b6f

File tree

6 files changed

+54
-21
lines changed

6 files changed

+54
-21
lines changed

news/+at-build.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
增加了 `At.build` 方法。

news/+client-send.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`Client` 的消息发送方法支持更多样的类型,包括单个字符串/元素/元素列表等。

news/+client-sigs.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`Client` 中好友和群组的部分 API 支持直接传入 `Friend``Group` 对象。

news/+forward-msg-build.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
增加了 `ForwardMessage.build` 方法以方便构建转发消息。

python/ichika/client.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"""基于 `ichika.core.PlumbingClient` 封装的高层 API"""
22
from __future__ import annotations
33

4-
from typing import Any, Awaitable, Callable, Literal, Protocol
4+
from typing import Any, Awaitable, Callable, Iterable, Literal, Protocol
55
from weakref import WeakValueDictionary
66

77
from graia.amnesia.message import Element, MessageChain
88

9-
from .core import PlumbingClient, RawMessageReceipt
9+
from .core import Friend, Group, PlumbingClient, RawMessageReceipt
1010
from .message import _serialize_message as _serialize_msg
1111
from .message.elements import (
1212
At,
@@ -41,54 +41,67 @@ def __call__(
4141
...
4242

4343

44+
def _uin(obj: Friend | Group | int) -> int:
45+
return obj if isinstance(obj, int) else obj.uin
46+
47+
48+
def _chain_coerce(msg: str | Element | MessageChain | Iterable[str | Element]) -> MessageChain:
49+
if isinstance(msg, MessageChain):
50+
return msg
51+
if isinstance(msg, (str, Element)):
52+
msg = [msg]
53+
if isinstance(msg, Iterable):
54+
return MessageChain([Text(e) if isinstance(e, str) else e for e in msg])
55+
56+
4457
class Client(PlumbingClient):
4558
"""基于 [`PlumbingClient`][ichika.core.PlumbingClient] 封装的高层 API"""
4659

47-
async def upload_friend_image(self, uin: int, data: bytes) -> Image:
60+
async def upload_friend_image(self, friend: int | Friend, data: bytes) -> Image:
4861
"""上传好友图片
4962
50-
:param uin: 好友 QQ
63+
:param friend: 好友 QQ 号或好友对象
5164
:param data: 图片数据
5265
5366
:return: 图片元素
5467
"""
55-
image_dict = await super().upload_friend_image(uin, data)
68+
image_dict = await super().upload_friend_image(_uin(friend), data)
5669
image_dict.pop("type")
5770
return Image(**image_dict)
5871

59-
async def upload_friend_audio(self, uin: int, data: bytes) -> Audio:
72+
async def upload_friend_audio(self, friend: int | Friend, data: bytes) -> Audio:
6073
"""上传好友语音
6174
62-
:param uin: 好友 QQ
75+
:param friend: 好友 QQ 号或好友对象
6376
:param data: 语音数据,应为 SILK/AMR 编码的音频数据
6477
6578
:return: 语音元素
6679
"""
67-
audio_dict = await super().upload_friend_audio(uin, data)
80+
audio_dict = await super().upload_friend_audio(_uin(friend), data)
6881
audio_dict.pop("type")
6982
return Audio(**audio_dict)
7083

71-
async def upload_group_image(self, uin: int, data: bytes) -> Image:
84+
async def upload_group_image(self, group: int | Group, data: bytes) -> Image:
7285
"""上传群图片
7386
74-
:param uin: 群号
87+
:param group: 群号或群对象
7588
:param data: 图片数据
7689
7790
:return: 图片元素
7891
"""
79-
image_dict = await super().upload_group_image(uin, data)
92+
image_dict = await super().upload_group_image(_uin(group), data)
8093
image_dict.pop("type")
8194
return Image(**image_dict)
8295

83-
async def upload_group_audio(self, uin: int, data: bytes) -> Audio:
96+
async def upload_group_audio(self, group: int | Group, data: bytes) -> Audio:
8497
"""上传群语音
8598
86-
:param uin: 群号
99+
:param group: 群号或群对象
87100
:param data: 语音数据,应为 SILK/AMR 编码的音频数据
88101
89102
:return: 语音元素
90103
"""
91-
audio_dict = await super().upload_group_audio(uin, data)
104+
audio_dict = await super().upload_group_audio(_uin(group), data)
92105
audio_dict.pop("type")
93106
return Audio(**audio_dict)
94107

@@ -149,16 +162,16 @@ async def _prepare_forward(self, uin: int, fwd: ForwardMessage) -> dict[str, Any
149162
data["content"] = [await self._prepare_forward(uin, f) for f in fwd.content]
150163
return data
151164

152-
async def upload_forward_msg(self, group_uin: int, msgs: list[ForwardMessage]) -> ForwardCard:
165+
async def upload_forward_msg(self, group: int | Group, msgs: list[ForwardMessage]) -> ForwardCard:
153166
"""上传合并转发消息
154167
155-
:param group_uin: 用于标记的原始群号
168+
:param group: 用于标记的原始群号或群对象
156169
:param msgs: 转发消息列表
157170
158171
:return: 转发卡片元素
159172
"""
160173
res_id, file_name, content = await super().upload_forward_msg(
161-
group_uin, [await self._prepare_forward(group_uin, msg) for msg in msgs]
174+
_uin(group), [await self._prepare_forward(_uin(group), msg) for msg in msgs]
162175
)
163176
return ForwardCard(res_id, file_name, content)
164177

@@ -178,28 +191,36 @@ async def _send_special_element(self, uin: int, kind: str, element: Element) ->
178191

179192
raise TypeError(f"无法发送元素: {element!r}")
180193

181-
async def send_group_message(self, uin: int, chain: MessageChain) -> RawMessageReceipt:
194+
async def send_group_message(
195+
self, group: int | Group, chain: str | Element | MessageChain | Iterable[str | Element]
196+
) -> RawMessageReceipt:
182197
"""发送群消息
183198
184-
:param uin: 群号
199+
:param group: 群号或群对象
185200
:param chain: 消息链
186201
187202
:return: 消息发送凭据,可用于撤回
188203
"""
204+
uin: int = _uin(group)
205+
chain = _chain_coerce(chain)
189206
if isinstance(validated := self._validate_chain(chain), Element):
190207
return await self._send_special_element(uin, "group", validated)
191208
for idx, elem in enumerate(chain):
192209
chain.content[idx] = await self._validate_mm(uin, elem, self.upload_group_image)
193210
return await super().send_group_message(uin, _serialize_msg(chain))
194211

195-
async def send_friend_message(self, uin: int, chain: MessageChain) -> RawMessageReceipt:
212+
async def send_friend_message(
213+
self, friend: int | Friend, chain: str | Element | MessageChain | Iterable[str | Element]
214+
) -> RawMessageReceipt:
196215
"""发送好友消息
197216
198-
:param uin: 好友 QQ
217+
:param friend: 好友 QQ 号或好友对象
199218
:param chain: 消息链
200219
201220
:return: 消息发送凭据,可用于撤回
202221
"""
222+
uin: int = _uin(friend)
223+
chain = _chain_coerce(chain)
203224
if isinstance(validated := self._validate_chain(chain), Element):
204225
return await self._send_special_element(uin, "friend", validated)
205226
for idx, elem in enumerate(chain):

python/ichika/message/elements.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ class At(Element):
5353
def __str__(self) -> str:
5454
return self.display or f"@{self.target}"
5555

56+
@classmethod
57+
def build(cls, obj: core.Member) -> At:
58+
return cls(obj.uin, obj.card_name)
59+
5660

5761
@dataclass
5862
class AtAll(Element):
@@ -234,6 +238,10 @@ class ForwardMessage:
234238
content: MessageChain | list[ForwardMessage]
235239
"""消息内容"""
236240

241+
@classmethod
242+
def build(cls, sender: core.Friend | core.Member, time: datetime, content: MessageChain) -> ForwardMessage:
243+
return cls(sender.uin, time, sender.card_name if isinstance(sender, core.Member) else sender.nick, content)
244+
237245

238246
@dataclass
239247
class RichMessage(Element):

0 commit comments

Comments
 (0)