hyperot-adapter-onebot 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hyperot_adapter_onebot/__init__.py +5 -0
- hyperot_adapter_onebot/actions.py +650 -0
- hyperot_adapter_onebot/adapter.py +193 -0
- hyperot_adapter_onebot/api.py +177 -0
- hyperot_adapter_onebot/config.py +51 -0
- hyperot_adapter_onebot/events.py +489 -0
- hyperot_adapter_onebot/manifest.json +12 -0
- hyperot_adapter_onebot/segments.py +566 -0
- hyperot_adapter_onebot/transport.py +402 -0
- hyperot_adapter_onebot-2.0.0.dist-info/METADATA +33 -0
- hyperot_adapter_onebot-2.0.0.dist-info/RECORD +14 -0
- hyperot_adapter_onebot-2.0.0.dist-info/WHEEL +5 -0
- hyperot_adapter_onebot-2.0.0.dist-info/entry_points.txt +2 -0
- hyperot_adapter_onebot-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Any, ClassVar, Protocol, TypeVar
|
|
5
|
+
|
|
6
|
+
from pydantic import JsonValue
|
|
7
|
+
from typing_extensions import override
|
|
8
|
+
|
|
9
|
+
from hyperot.v2.actions import (
|
|
10
|
+
Action,
|
|
11
|
+
ApproveFriendRequestAction,
|
|
12
|
+
ApproveGroupRequestAction,
|
|
13
|
+
BotProfile,
|
|
14
|
+
BotStatus,
|
|
15
|
+
CookieInfo,
|
|
16
|
+
CsrfTokenInfo,
|
|
17
|
+
FetchMessageAction,
|
|
18
|
+
FileUrl,
|
|
19
|
+
FriendInfo,
|
|
20
|
+
GetBotProfileAction,
|
|
21
|
+
GetBotStatusAction,
|
|
22
|
+
GetCookieAction,
|
|
23
|
+
GetCsrfTokenAction,
|
|
24
|
+
GetFriendListAction,
|
|
25
|
+
GetGroupListAction,
|
|
26
|
+
GetGroupMemberAction,
|
|
27
|
+
GetGroupMemberListAction,
|
|
28
|
+
GetGroupProfileAction,
|
|
29
|
+
GetUserProfileAction,
|
|
30
|
+
GetVersionAction,
|
|
31
|
+
GroupMemberProfile,
|
|
32
|
+
GroupProfile,
|
|
33
|
+
KickMemberAction,
|
|
34
|
+
LeaveGroupAction,
|
|
35
|
+
MuteMemberAction,
|
|
36
|
+
PokeAction,
|
|
37
|
+
RawAction,
|
|
38
|
+
RawResult,
|
|
39
|
+
ReactMessageAction,
|
|
40
|
+
RecallMessageAction,
|
|
41
|
+
RejectFriendRequestAction,
|
|
42
|
+
RejectGroupRequestAction,
|
|
43
|
+
SendLikeAction,
|
|
44
|
+
SendMessageAction,
|
|
45
|
+
SendResult,
|
|
46
|
+
SetEssenceAction,
|
|
47
|
+
SetGroupMuteAction,
|
|
48
|
+
SetGroupNameAction,
|
|
49
|
+
SetMemberCardAction,
|
|
50
|
+
SetMemberRoleAction,
|
|
51
|
+
SetMemberTitleAction,
|
|
52
|
+
UnmuteMemberAction,
|
|
53
|
+
UserProfile,
|
|
54
|
+
VersionInfo,
|
|
55
|
+
)
|
|
56
|
+
from hyperot.v2.actions.formatting import (
|
|
57
|
+
format_message,
|
|
58
|
+
format_source,
|
|
59
|
+
format_text,
|
|
60
|
+
)
|
|
61
|
+
from hyperot.v2.adapter import ActionRegistry
|
|
62
|
+
from hyperot.v2.common import (
|
|
63
|
+
ActionRejectedError,
|
|
64
|
+
FileId,
|
|
65
|
+
GroupId,
|
|
66
|
+
MemberRole,
|
|
67
|
+
MessageId,
|
|
68
|
+
SceneType,
|
|
69
|
+
UserId,
|
|
70
|
+
)
|
|
71
|
+
from hyperot.v2.messages import Message
|
|
72
|
+
|
|
73
|
+
from .segments import OneBotSegmentCodec
|
|
74
|
+
|
|
75
|
+
ResultT = TypeVar("ResultT")
|
|
76
|
+
|
|
77
|
+
ONEBOT_ENDPOINTS = frozenset(
|
|
78
|
+
{
|
|
79
|
+
"send_msg",
|
|
80
|
+
"send_private_msg",
|
|
81
|
+
"send_group_msg",
|
|
82
|
+
"delete_msg",
|
|
83
|
+
"get_msg",
|
|
84
|
+
"get_forward_msg",
|
|
85
|
+
"send_like",
|
|
86
|
+
"send_poke",
|
|
87
|
+
"set_group_kick",
|
|
88
|
+
"set_group_ban",
|
|
89
|
+
"set_group_whole_ban",
|
|
90
|
+
"set_group_admin",
|
|
91
|
+
"set_group_card",
|
|
92
|
+
"set_group_name",
|
|
93
|
+
"set_group_leave",
|
|
94
|
+
"set_group_special_title",
|
|
95
|
+
"set_friend_add_request",
|
|
96
|
+
"set_group_add_request",
|
|
97
|
+
"get_login_info",
|
|
98
|
+
"get_stranger_info",
|
|
99
|
+
"get_friend_list",
|
|
100
|
+
"get_group_info",
|
|
101
|
+
"get_group_list",
|
|
102
|
+
"get_group_member_info",
|
|
103
|
+
"get_group_member_list",
|
|
104
|
+
"get_status",
|
|
105
|
+
"get_version_info",
|
|
106
|
+
"get_cookies",
|
|
107
|
+
"get_csrf_token",
|
|
108
|
+
"group_reaction",
|
|
109
|
+
"upload_group_file",
|
|
110
|
+
"upload_private_file",
|
|
111
|
+
"get_group_file_url",
|
|
112
|
+
"get_private_file_url",
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class ActionTransport(Protocol):
|
|
118
|
+
async def call(self, action: str, params: dict[str, Any], timeout: float) -> dict[str, Any]: ...
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
MessageGroupLookup = Callable[[MessageId], GroupId | None]
|
|
122
|
+
MessageGroupRemember = Callable[[MessageId, GroupId], None]
|
|
123
|
+
RequestSubtypeLookup = Callable[[str], str | None]
|
|
124
|
+
FileContextLookup = Callable[[FileId], dict[str, JsonValue] | None]
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class OneBotKickMemberAction(KickMemberAction):
|
|
128
|
+
reject_add_request: bool = False
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class OneBotSetMemberTitleAction(SetMemberTitleAction):
|
|
132
|
+
duration: int = -1
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class OneBotLeaveGroupAction(LeaveGroupAction):
|
|
136
|
+
is_dismiss: bool = False
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class SendPrivateMessageAction(Action[SendResult]):
|
|
140
|
+
user_id: UserId
|
|
141
|
+
message: Message
|
|
142
|
+
|
|
143
|
+
@override
|
|
144
|
+
def log_summary(self) -> str:
|
|
145
|
+
return f"[user] {format_text(str(self.user_id))} send: {format_message(self.message)}"
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class SendGroupMessageAction(Action[SendResult]):
|
|
149
|
+
group_id: GroupId
|
|
150
|
+
message: Message
|
|
151
|
+
|
|
152
|
+
@override
|
|
153
|
+
def log_summary(self) -> str:
|
|
154
|
+
return f"[group] {format_text(str(self.group_id))} send: {format_message(self.message)}"
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class GetForwardMessageAction(Action[Message]):
|
|
158
|
+
log_level: ClassVar[str] = "TRACE"
|
|
159
|
+
forward_id: str
|
|
160
|
+
|
|
161
|
+
@override
|
|
162
|
+
def log_summary(self) -> str:
|
|
163
|
+
return f"fetch forward {format_text(self.forward_id)}"
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class GroupReactionAction(Action[None]):
|
|
167
|
+
group_id: GroupId
|
|
168
|
+
message_id: MessageId
|
|
169
|
+
reaction: str
|
|
170
|
+
enabled: bool = True
|
|
171
|
+
|
|
172
|
+
@override
|
|
173
|
+
def log_summary(self) -> str:
|
|
174
|
+
action = "react" if self.enabled else "unreact"
|
|
175
|
+
return f"{action} message {format_text(str(self.message_id))} with {format_text(self.reaction)}"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class UploadGroupFileAction(Action[None]):
|
|
179
|
+
group_id: GroupId
|
|
180
|
+
file: str
|
|
181
|
+
name: str | None = None
|
|
182
|
+
folder: str | None = None
|
|
183
|
+
|
|
184
|
+
@override
|
|
185
|
+
def log_summary(self) -> str:
|
|
186
|
+
name = self.name or format_source(self.file)
|
|
187
|
+
folder = f" folder={format_text(self.folder)}" if self.folder else ""
|
|
188
|
+
return f"[group] {format_text(str(self.group_id))} upload file {name}{folder}"
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class UploadPrivateFileAction(Action[None]):
|
|
192
|
+
user_id: UserId
|
|
193
|
+
file: str
|
|
194
|
+
name: str | None = None
|
|
195
|
+
|
|
196
|
+
@override
|
|
197
|
+
def log_summary(self) -> str:
|
|
198
|
+
name = self.name or format_source(self.file)
|
|
199
|
+
return f"[user] {format_text(str(self.user_id))} upload file {name}"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class GetGroupFileUrlAction(Action[FileUrl]):
|
|
203
|
+
log_level: ClassVar[str] = "TRACE"
|
|
204
|
+
group_id: GroupId
|
|
205
|
+
file_id: FileId
|
|
206
|
+
|
|
207
|
+
@override
|
|
208
|
+
def log_summary(self) -> str:
|
|
209
|
+
return f"[group] {format_text(str(self.group_id))} file {format_text(str(self.file_id))} url"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class GetPrivateFileUrlAction(Action[FileUrl]):
|
|
213
|
+
log_level: ClassVar[str] = "TRACE"
|
|
214
|
+
user_id: UserId
|
|
215
|
+
file_id: FileId
|
|
216
|
+
file_hash: str | None = None
|
|
217
|
+
|
|
218
|
+
@override
|
|
219
|
+
def log_summary(self) -> str:
|
|
220
|
+
return f"[user] {format_text(str(self.user_id))} file {format_text(str(self.file_id))} url"
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class OneBotActions:
|
|
224
|
+
def __init__(
|
|
225
|
+
self,
|
|
226
|
+
transport: ActionTransport,
|
|
227
|
+
codec: OneBotSegmentCodec,
|
|
228
|
+
action_timeout: float,
|
|
229
|
+
*,
|
|
230
|
+
message_group_lookup: MessageGroupLookup | None = None,
|
|
231
|
+
message_group_remember: MessageGroupRemember | None = None,
|
|
232
|
+
request_subtype_lookup: RequestSubtypeLookup | None = None,
|
|
233
|
+
file_context_lookup: FileContextLookup | None = None,
|
|
234
|
+
) -> None:
|
|
235
|
+
self.transport = transport
|
|
236
|
+
self.codec = codec
|
|
237
|
+
self.action_timeout = action_timeout
|
|
238
|
+
self.message_group_lookup = message_group_lookup
|
|
239
|
+
self.message_group_remember = message_group_remember
|
|
240
|
+
self.request_subtype_lookup = request_subtype_lookup
|
|
241
|
+
self.file_context_lookup = file_context_lookup
|
|
242
|
+
|
|
243
|
+
def register_all(self, registry: ActionRegistry) -> None:
|
|
244
|
+
registry.register(RawAction, self.raw)
|
|
245
|
+
registry.register(SendMessageAction, self.send_message)
|
|
246
|
+
registry.register(SendPrivateMessageAction, self.send_private_message)
|
|
247
|
+
registry.register(SendGroupMessageAction, self.send_group_message)
|
|
248
|
+
registry.register(RecallMessageAction, self.recall_message)
|
|
249
|
+
registry.register(FetchMessageAction, self.fetch_message)
|
|
250
|
+
registry.register(GetForwardMessageAction, self.get_forward_message)
|
|
251
|
+
registry.register(GetBotProfileAction, self.get_bot_profile)
|
|
252
|
+
registry.register(GetBotStatusAction, self.get_bot_status)
|
|
253
|
+
registry.register(GetVersionAction, self.get_version)
|
|
254
|
+
registry.register(GetUserProfileAction, self.get_user_profile)
|
|
255
|
+
registry.register(GetFriendListAction, self.get_friend_list)
|
|
256
|
+
registry.register(GetGroupProfileAction, self.get_group_profile)
|
|
257
|
+
registry.register(GetGroupListAction, self.get_group_list)
|
|
258
|
+
registry.register(GetGroupMemberAction, self.get_group_member)
|
|
259
|
+
registry.register(GetGroupMemberListAction, self.get_group_member_list)
|
|
260
|
+
registry.register(KickMemberAction, self.kick_member)
|
|
261
|
+
registry.register(OneBotKickMemberAction, self.kick_member)
|
|
262
|
+
registry.register(MuteMemberAction, self.mute_member)
|
|
263
|
+
registry.register(UnmuteMemberAction, self.unmute_member)
|
|
264
|
+
registry.register(SetMemberRoleAction, self.set_member_role)
|
|
265
|
+
registry.register(SetMemberTitleAction, self.set_member_title)
|
|
266
|
+
registry.register(OneBotSetMemberTitleAction, self.set_member_title)
|
|
267
|
+
registry.register(SetMemberCardAction, self.set_member_card)
|
|
268
|
+
registry.register(SetGroupNameAction, self.set_group_name)
|
|
269
|
+
registry.register(SetGroupMuteAction, self.set_group_mute)
|
|
270
|
+
registry.register(LeaveGroupAction, self.leave_group)
|
|
271
|
+
registry.register(OneBotLeaveGroupAction, self.leave_group)
|
|
272
|
+
registry.register(ReactMessageAction, self.react_message)
|
|
273
|
+
registry.register(SetEssenceAction, self.set_essence)
|
|
274
|
+
registry.register(PokeAction, self.poke)
|
|
275
|
+
registry.register(SendLikeAction, self.send_like)
|
|
276
|
+
registry.register(GetCookieAction, self.get_cookie)
|
|
277
|
+
registry.register(GetCsrfTokenAction, self.get_csrf_token)
|
|
278
|
+
registry.register(ApproveFriendRequestAction, self.approve_friend_request)
|
|
279
|
+
registry.register(RejectFriendRequestAction, self.reject_friend_request)
|
|
280
|
+
registry.register(ApproveGroupRequestAction, self.approve_group_request)
|
|
281
|
+
registry.register(RejectGroupRequestAction, self.reject_group_request)
|
|
282
|
+
registry.register(UploadGroupFileAction, self.upload_group_file)
|
|
283
|
+
registry.register(UploadPrivateFileAction, self.upload_private_file)
|
|
284
|
+
registry.register(GetGroupFileUrlAction, self.get_group_file_url)
|
|
285
|
+
registry.register(GetPrivateFileUrlAction, self.get_private_file_url)
|
|
286
|
+
registry.register(GroupReactionAction, self.group_reaction)
|
|
287
|
+
|
|
288
|
+
async def raw(self, action: RawAction) -> RawResult:
|
|
289
|
+
data = await self._call_raw(action.action, dict(action.params))
|
|
290
|
+
return RawResult(data=data)
|
|
291
|
+
|
|
292
|
+
async def _call_raw(self, endpoint: str, params: dict[str, Any]) -> JsonValue:
|
|
293
|
+
response = await self.transport.call(endpoint, params, self.action_timeout)
|
|
294
|
+
status = response.get("status")
|
|
295
|
+
retcode = int(response.get("retcode", 0))
|
|
296
|
+
if status not in {None, "ok", "async"} or retcode not in {0, 1}:
|
|
297
|
+
raise ActionRejectedError(f"{endpoint} failed with retcode={retcode}: {response.get('message', '')}")
|
|
298
|
+
return response.get("data")
|
|
299
|
+
|
|
300
|
+
async def _call(self, endpoint: str, params: dict[str, Any]) -> dict[str, Any]:
|
|
301
|
+
data = await self._call_raw(endpoint, params)
|
|
302
|
+
if data is None:
|
|
303
|
+
return {}
|
|
304
|
+
if not isinstance(data, dict):
|
|
305
|
+
return {"data": data}
|
|
306
|
+
return data
|
|
307
|
+
|
|
308
|
+
async def send_message(self, action: SendMessageAction) -> SendResult:
|
|
309
|
+
params: dict[str, Any] = {"message": self.codec.encode_message(action.message)}
|
|
310
|
+
if action.scene_type == SceneType.GROUP:
|
|
311
|
+
params["group_id"] = str(action.scene_id)
|
|
312
|
+
else:
|
|
313
|
+
params["user_id"] = str(action.scene_id)
|
|
314
|
+
data = await self._call("send_msg", params)
|
|
315
|
+
message_id = MessageId(str(data["message_id"]))
|
|
316
|
+
if action.scene_type == SceneType.GROUP and self.message_group_remember is not None:
|
|
317
|
+
self.message_group_remember(message_id, GroupId(str(action.scene_id)))
|
|
318
|
+
return SendResult(message_id=message_id)
|
|
319
|
+
|
|
320
|
+
async def send_private_message(self, action: SendPrivateMessageAction) -> SendResult:
|
|
321
|
+
data = await self._call(
|
|
322
|
+
"send_private_msg",
|
|
323
|
+
{"user_id": str(action.user_id), "message": self.codec.encode_message(action.message)},
|
|
324
|
+
)
|
|
325
|
+
return SendResult(message_id=MessageId(str(data["message_id"])))
|
|
326
|
+
|
|
327
|
+
async def send_group_message(self, action: SendGroupMessageAction) -> SendResult:
|
|
328
|
+
data = await self._call(
|
|
329
|
+
"send_group_msg",
|
|
330
|
+
{"group_id": str(action.group_id), "message": self.codec.encode_message(action.message)},
|
|
331
|
+
)
|
|
332
|
+
message_id = MessageId(str(data["message_id"]))
|
|
333
|
+
if self.message_group_remember is not None:
|
|
334
|
+
self.message_group_remember(message_id, action.group_id)
|
|
335
|
+
return SendResult(message_id=message_id)
|
|
336
|
+
|
|
337
|
+
async def recall_message(self, action: RecallMessageAction) -> None:
|
|
338
|
+
await self._call("delete_msg", {"message_id": str(action.message_id)})
|
|
339
|
+
|
|
340
|
+
async def fetch_message(self, action: FetchMessageAction) -> Message:
|
|
341
|
+
data = await self._call("get_msg", {"message_id": str(action.message_id)})
|
|
342
|
+
return self.codec.decode_message(data.get("message", []))
|
|
343
|
+
|
|
344
|
+
async def get_forward_message(self, action: GetForwardMessageAction) -> Message:
|
|
345
|
+
data = await self._call("get_forward_msg", {"id": action.forward_id})
|
|
346
|
+
nodes = []
|
|
347
|
+
for node in data.get("message", []):
|
|
348
|
+
node_data = node.get("data", node)
|
|
349
|
+
nodes.append(
|
|
350
|
+
{
|
|
351
|
+
"type": "node",
|
|
352
|
+
"data": {
|
|
353
|
+
"user_id": node_data.get("user_id", ""),
|
|
354
|
+
"nickname": node_data.get("nickname", ""),
|
|
355
|
+
"content": node_data.get("content", []),
|
|
356
|
+
},
|
|
357
|
+
}
|
|
358
|
+
)
|
|
359
|
+
return self.codec.decode_message(nodes)
|
|
360
|
+
|
|
361
|
+
async def get_bot_profile(self, _action: GetBotProfileAction) -> BotProfile:
|
|
362
|
+
data = await self._call("get_login_info", {})
|
|
363
|
+
return BotProfile(
|
|
364
|
+
user_id=UserId(str(data.get("user_id", 0))),
|
|
365
|
+
display_name=str(data.get("nickname", "")),
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
async def get_bot_status(self, _action: GetBotStatusAction) -> BotStatus:
|
|
369
|
+
data = await self._call("get_status", {})
|
|
370
|
+
return BotStatus(
|
|
371
|
+
online=bool(data.get("online", True)),
|
|
372
|
+
good=bool(data.get("good", True)),
|
|
373
|
+
app_good=bool(data.get("app_good", True)),
|
|
374
|
+
memory=int(data.get("memory", 0)),
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
async def get_version(self, _action: GetVersionAction) -> VersionInfo:
|
|
378
|
+
data = await self._call("get_version_info", {})
|
|
379
|
+
return VersionInfo(
|
|
380
|
+
app_name=str(data.get("app_name", "")),
|
|
381
|
+
app_version=str(data.get("app_version", "")),
|
|
382
|
+
protocol_version=str(data.get("protocol_version", "")),
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
async def get_user_profile(self, action: GetUserProfileAction) -> UserProfile:
|
|
386
|
+
data = await self._call("get_stranger_info", {"user_id": str(action.user_id)})
|
|
387
|
+
return UserProfile(
|
|
388
|
+
user_id=UserId(str(data.get("user_id", action.user_id))),
|
|
389
|
+
display_name=str(data.get("nickname", "")),
|
|
390
|
+
sex=str(data.get("sex", "")),
|
|
391
|
+
age=int(data.get("age", 0)),
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
async def get_friend_list(self, _action: GetFriendListAction) -> list[FriendInfo]:
|
|
395
|
+
data = await self._call("get_friend_list", {})
|
|
396
|
+
items = data.get("data", data) if "data" in data else data
|
|
397
|
+
if isinstance(items, dict):
|
|
398
|
+
items = items.get("list", [])
|
|
399
|
+
return [
|
|
400
|
+
FriendInfo(
|
|
401
|
+
user_id=UserId(str(item.get("user_id", 0))),
|
|
402
|
+
display_name=str(item.get("nickname", "")),
|
|
403
|
+
)
|
|
404
|
+
for item in items
|
|
405
|
+
]
|
|
406
|
+
|
|
407
|
+
async def get_group_profile(self, action: GetGroupProfileAction) -> GroupProfile:
|
|
408
|
+
data = await self._call("get_group_info", {"group_id": str(action.group_id)})
|
|
409
|
+
return GroupProfile(
|
|
410
|
+
group_id=GroupId(str(data.get("group_id", action.group_id))),
|
|
411
|
+
name=str(data.get("group_name", "")),
|
|
412
|
+
member_count=int(data.get("member_count", 0)),
|
|
413
|
+
max_member_count=int(data.get("max_member_count", 0)),
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
async def get_group_list(self, _action: GetGroupListAction) -> list[GroupProfile]:
|
|
417
|
+
data = await self._call("get_group_list", {})
|
|
418
|
+
items = data.get("data", data) if "data" in data else data
|
|
419
|
+
if isinstance(items, dict):
|
|
420
|
+
items = items.get("list", [])
|
|
421
|
+
return [
|
|
422
|
+
GroupProfile(
|
|
423
|
+
group_id=GroupId(str(item.get("group_id", 0))),
|
|
424
|
+
name=str(item.get("group_name", "")),
|
|
425
|
+
member_count=int(item.get("member_count", 0)),
|
|
426
|
+
max_member_count=int(item.get("max_member_count", 0)),
|
|
427
|
+
)
|
|
428
|
+
for item in items
|
|
429
|
+
]
|
|
430
|
+
|
|
431
|
+
async def get_group_member(self, action: GetGroupMemberAction) -> GroupMemberProfile:
|
|
432
|
+
data = await self._call(
|
|
433
|
+
"get_group_member_info",
|
|
434
|
+
{"group_id": str(action.group_id), "user_id": str(action.user_id)},
|
|
435
|
+
)
|
|
436
|
+
return GroupMemberProfile(
|
|
437
|
+
group_id=GroupId(str(data.get("group_id", action.group_id))),
|
|
438
|
+
user_id=UserId(str(data.get("user_id", action.user_id))),
|
|
439
|
+
display_name=str(data.get("nickname", "")),
|
|
440
|
+
card=str(data.get("card", "")),
|
|
441
|
+
role=_member_role(data.get("role")),
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
async def get_group_member_list(self, action: GetGroupMemberListAction) -> list[GroupMemberProfile]:
|
|
445
|
+
data = await self._call("get_group_member_list", {"group_id": str(action.group_id)})
|
|
446
|
+
items = data.get("data", data) if "data" in data else data
|
|
447
|
+
if isinstance(items, dict):
|
|
448
|
+
items = items.get("list", [])
|
|
449
|
+
return [
|
|
450
|
+
GroupMemberProfile(
|
|
451
|
+
group_id=GroupId(str(item.get("group_id", action.group_id))),
|
|
452
|
+
user_id=UserId(str(item.get("user_id", 0))),
|
|
453
|
+
display_name=str(item.get("nickname", "")),
|
|
454
|
+
card=str(item.get("card", "")),
|
|
455
|
+
role=_member_role(item.get("role")),
|
|
456
|
+
)
|
|
457
|
+
for item in items
|
|
458
|
+
]
|
|
459
|
+
|
|
460
|
+
async def kick_member(self, action: KickMemberAction) -> None:
|
|
461
|
+
params: dict[str, Any] = {"group_id": str(action.group_id), "user_id": str(action.user_id)}
|
|
462
|
+
if isinstance(action, OneBotKickMemberAction):
|
|
463
|
+
params["reject_add_request"] = action.reject_add_request
|
|
464
|
+
await self._call("set_group_kick", params)
|
|
465
|
+
|
|
466
|
+
async def mute_member(self, action: MuteMemberAction) -> None:
|
|
467
|
+
await self._call(
|
|
468
|
+
"set_group_ban",
|
|
469
|
+
{"group_id": str(action.group_id), "user_id": str(action.user_id), "duration": action.duration},
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
async def unmute_member(self, action: UnmuteMemberAction) -> None:
|
|
473
|
+
await self._call(
|
|
474
|
+
"set_group_ban",
|
|
475
|
+
{"group_id": str(action.group_id), "user_id": str(action.user_id), "duration": 0},
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
async def set_member_role(self, action: SetMemberRoleAction) -> None:
|
|
479
|
+
await self._call(
|
|
480
|
+
"set_group_admin",
|
|
481
|
+
{
|
|
482
|
+
"group_id": str(action.group_id),
|
|
483
|
+
"user_id": str(action.user_id),
|
|
484
|
+
"enable": action.role != MemberRole.MEMBER,
|
|
485
|
+
},
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
async def set_member_title(self, action: SetMemberTitleAction) -> None:
|
|
489
|
+
params: dict[str, Any] = {
|
|
490
|
+
"group_id": str(action.group_id),
|
|
491
|
+
"user_id": str(action.user_id),
|
|
492
|
+
"special_title": action.title,
|
|
493
|
+
}
|
|
494
|
+
if isinstance(action, OneBotSetMemberTitleAction):
|
|
495
|
+
params["duration"] = action.duration
|
|
496
|
+
await self._call("set_group_special_title", params)
|
|
497
|
+
|
|
498
|
+
async def set_member_card(self, action: SetMemberCardAction) -> None:
|
|
499
|
+
await self._call(
|
|
500
|
+
"set_group_card",
|
|
501
|
+
{"group_id": str(action.group_id), "user_id": str(action.user_id), "card": action.card},
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
async def set_group_name(self, action: SetGroupNameAction) -> None:
|
|
505
|
+
await self._call("set_group_name", {"group_id": str(action.group_id), "group_name": action.name})
|
|
506
|
+
|
|
507
|
+
async def set_group_mute(self, action: SetGroupMuteAction) -> None:
|
|
508
|
+
await self._call("set_group_whole_ban", {"group_id": str(action.group_id), "enable": action.muted})
|
|
509
|
+
|
|
510
|
+
async def leave_group(self, action: LeaveGroupAction) -> None:
|
|
511
|
+
params: dict[str, Any] = {"group_id": str(action.group_id)}
|
|
512
|
+
if isinstance(action, OneBotLeaveGroupAction):
|
|
513
|
+
params["is_dismiss"] = action.is_dismiss
|
|
514
|
+
await self._call("set_group_leave", params)
|
|
515
|
+
|
|
516
|
+
async def react_message(self, action: ReactMessageAction) -> None:
|
|
517
|
+
if self.message_group_lookup is None:
|
|
518
|
+
raise ActionRejectedError("OneBot reaction requires message context")
|
|
519
|
+
group_id = self.message_group_lookup(action.message_id)
|
|
520
|
+
if group_id is None:
|
|
521
|
+
raise ActionRejectedError(f"OneBot reaction requires group context for message {action.message_id}")
|
|
522
|
+
await self._send_group_reaction(group_id, action.message_id, action.reaction, action.enabled)
|
|
523
|
+
|
|
524
|
+
async def group_reaction(self, action: GroupReactionAction) -> None:
|
|
525
|
+
await self._send_group_reaction(action.group_id, action.message_id, action.reaction, action.enabled)
|
|
526
|
+
|
|
527
|
+
async def _send_group_reaction(
|
|
528
|
+
self,
|
|
529
|
+
group_id: GroupId,
|
|
530
|
+
message_id: MessageId,
|
|
531
|
+
reaction: str,
|
|
532
|
+
enabled: bool,
|
|
533
|
+
) -> None:
|
|
534
|
+
params: dict[str, Any] = {
|
|
535
|
+
"group_id": str(group_id),
|
|
536
|
+
"message_id": str(message_id),
|
|
537
|
+
"is_add": enabled,
|
|
538
|
+
}
|
|
539
|
+
if reaction.lstrip("-").isdigit():
|
|
540
|
+
params["code"] = int(reaction)
|
|
541
|
+
else:
|
|
542
|
+
params["emoji"] = reaction
|
|
543
|
+
await self._call(
|
|
544
|
+
"group_reaction",
|
|
545
|
+
params,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
async def set_essence(self, action: SetEssenceAction) -> None:
|
|
549
|
+
endpoint = "set_essence_msg" if action.enabled else "delete_essence_msg"
|
|
550
|
+
await self._call(endpoint, {"message_id": str(action.message_id)})
|
|
551
|
+
|
|
552
|
+
async def poke(self, action: PokeAction) -> None:
|
|
553
|
+
params = {"user_id": str(action.user_id)}
|
|
554
|
+
if action.scene_type == SceneType.GROUP:
|
|
555
|
+
params["group_id"] = str(action.scene_id)
|
|
556
|
+
await self._call("send_poke", params)
|
|
557
|
+
|
|
558
|
+
async def send_like(self, action: SendLikeAction) -> None:
|
|
559
|
+
await self._call("send_like", {"user_id": str(action.user_id), "times": action.times})
|
|
560
|
+
|
|
561
|
+
async def get_cookie(self, action: GetCookieAction) -> CookieInfo:
|
|
562
|
+
data = await self._call("get_cookies", {"domain": action.domain})
|
|
563
|
+
return CookieInfo(cookies=str(data.get("cookies", "")))
|
|
564
|
+
|
|
565
|
+
async def get_csrf_token(self, _action: GetCsrfTokenAction) -> CsrfTokenInfo:
|
|
566
|
+
data = await self._call("get_csrf_token", {})
|
|
567
|
+
return CsrfTokenInfo(token=int(data.get("token", 0)))
|
|
568
|
+
|
|
569
|
+
async def approve_friend_request(self, action: ApproveFriendRequestAction) -> None:
|
|
570
|
+
await self._call("set_friend_add_request", {"flag": str(action.request_id), "approve": True})
|
|
571
|
+
|
|
572
|
+
async def reject_friend_request(self, action: RejectFriendRequestAction) -> None:
|
|
573
|
+
await self._call(
|
|
574
|
+
"set_friend_add_request",
|
|
575
|
+
{"flag": str(action.request_id), "approve": False, "remark": action.reason or ""},
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
async def approve_group_request(self, action: ApproveGroupRequestAction) -> None:
|
|
579
|
+
sub_type = self._request_subtype(str(action.request_id))
|
|
580
|
+
await self._call(
|
|
581
|
+
"set_group_add_request",
|
|
582
|
+
{"flag": str(action.request_id), "sub_type": sub_type, "approve": True},
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
async def reject_group_request(self, action: RejectGroupRequestAction) -> None:
|
|
586
|
+
sub_type = self._request_subtype(str(action.request_id))
|
|
587
|
+
await self._call(
|
|
588
|
+
"set_group_add_request",
|
|
589
|
+
{
|
|
590
|
+
"flag": str(action.request_id),
|
|
591
|
+
"sub_type": sub_type,
|
|
592
|
+
"approve": False,
|
|
593
|
+
"reason": action.reason or "",
|
|
594
|
+
},
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
async def upload_group_file(self, action: UploadGroupFileAction) -> None:
|
|
598
|
+
params = {"group_id": str(action.group_id), "file": action.file}
|
|
599
|
+
if action.name:
|
|
600
|
+
params["name"] = action.name
|
|
601
|
+
if action.folder:
|
|
602
|
+
params["folder"] = action.folder
|
|
603
|
+
await self._call("upload_group_file", params)
|
|
604
|
+
|
|
605
|
+
async def upload_private_file(self, action: UploadPrivateFileAction) -> None:
|
|
606
|
+
params = {"user_id": str(action.user_id), "file": action.file}
|
|
607
|
+
if action.name:
|
|
608
|
+
params["name"] = action.name
|
|
609
|
+
await self._call("upload_private_file", params)
|
|
610
|
+
|
|
611
|
+
async def get_group_file_url(self, action: GetGroupFileUrlAction) -> FileUrl:
|
|
612
|
+
context = self.file_context_lookup(action.file_id) if self.file_context_lookup is not None else None
|
|
613
|
+
params: dict[str, Any] = {"group_id": str(action.group_id), "file_id": str(action.file_id)}
|
|
614
|
+
if context is not None and "busid" in context:
|
|
615
|
+
params["busid"] = context["busid"]
|
|
616
|
+
data = await self._call("get_group_file_url", params)
|
|
617
|
+
return FileUrl(url=str(data.get("url", "")))
|
|
618
|
+
|
|
619
|
+
async def get_private_file_url(self, action: GetPrivateFileUrlAction) -> FileUrl:
|
|
620
|
+
params = {"user_id": str(action.user_id), "file_id": str(action.file_id)}
|
|
621
|
+
file_hash = action.file_hash
|
|
622
|
+
if file_hash is None and self.file_context_lookup is not None:
|
|
623
|
+
context = self.file_context_lookup(action.file_id)
|
|
624
|
+
if context is not None and context.get("file_hash"):
|
|
625
|
+
file_hash = str(context["file_hash"])
|
|
626
|
+
if not file_hash:
|
|
627
|
+
raise ActionRejectedError(f"OneBot private file URL requires file_hash for {action.file_id}")
|
|
628
|
+
params["file_hash"] = file_hash
|
|
629
|
+
data = await self._call("get_private_file_url", params)
|
|
630
|
+
return FileUrl(url=str(data.get("url", "")))
|
|
631
|
+
|
|
632
|
+
def _request_subtype(self, request_id: str) -> str:
|
|
633
|
+
if self.request_subtype_lookup is None:
|
|
634
|
+
return "add"
|
|
635
|
+
sub_type = self.request_subtype_lookup(request_id)
|
|
636
|
+
if sub_type not in {"add", "invite"}:
|
|
637
|
+
raise ActionRejectedError(f"OneBot group request subtype is unknown: {request_id}")
|
|
638
|
+
return sub_type
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def _member_role(value: object) -> MemberRole | None:
|
|
642
|
+
match value:
|
|
643
|
+
case "owner":
|
|
644
|
+
return MemberRole.OWNER
|
|
645
|
+
case "admin":
|
|
646
|
+
return MemberRole.ADMIN
|
|
647
|
+
case "member":
|
|
648
|
+
return MemberRole.MEMBER
|
|
649
|
+
case _:
|
|
650
|
+
return None
|