satori-python-adapter-qq 0.1.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.
- .mina/adapter_qq.toml +32 -0
- satori/adapters/qq/__init__.py +0 -0
- satori/adapters/qq/api.py +433 -0
- satori/adapters/qq/audit_store.py +47 -0
- satori/adapters/qq/events/__init__.py +5 -0
- satori/adapters/qq/events/base.py +17 -0
- satori/adapters/qq/events/group.py +56 -0
- satori/adapters/qq/events/guild.py +63 -0
- satori/adapters/qq/events/interaction.py +56 -0
- satori/adapters/qq/events/message.py +237 -0
- satori/adapters/qq/exception.py +68 -0
- satori/adapters/qq/main.py +442 -0
- satori/adapters/qq/message.py +467 -0
- satori/adapters/qq/utils.py +135 -0
- satori/adapters/qq/websocket.py +560 -0
- satori_python_adapter_qq-0.1.0.dist-info/METADATA +154 -0
- satori_python_adapter_qq-0.1.0.dist-info/RECORD +19 -0
- satori_python_adapter_qq-0.1.0.dist-info/WHEEL +4 -0
- satori_python_adapter_qq-0.1.0.dist-info/licenses/LICENSE +21 -0
.mina/adapter_qq.toml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
includes = ["src/satori/adapters/qq"]
|
|
2
|
+
raw-dependencies = [
|
|
3
|
+
"satori-python >= 0.18.0",
|
|
4
|
+
"crypography>=46.0.4",
|
|
5
|
+
]
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "satori-python-adapter-qq"
|
|
9
|
+
version = "0.1.0"
|
|
10
|
+
authors = [
|
|
11
|
+
{name = "RF-Tar-Railt", email = "rf_tar_railt@qq.com"}
|
|
12
|
+
]
|
|
13
|
+
dependencies = []
|
|
14
|
+
description = "Satori Protocol SDK for python, adapter for QQ"
|
|
15
|
+
license = {text = "MIT"}
|
|
16
|
+
readme = "README.md"
|
|
17
|
+
requires-python = ">=3.10,<4.0"
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Typing :: Typed",
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Programming Language :: Python :: 3.8",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
homepage = "https://github.com/RF-Tar-Railt/satori-python"
|
|
32
|
+
repository = "https://github.com/RF-Tar-Railt/satori-python"
|
|
File without changes
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
from satori import Api, At, Channel, ChannelType, Guild, Login, Member, MessageObject, PageResult, Role, Text
|
|
5
|
+
from satori.exception import BadRequestException, NotFoundException
|
|
6
|
+
from satori.server import Adapter, Request
|
|
7
|
+
from satori.server.route import (
|
|
8
|
+
ChannelCreateParam,
|
|
9
|
+
ChannelListParam,
|
|
10
|
+
ChannelParam,
|
|
11
|
+
ChannelUpdateParam,
|
|
12
|
+
GuildGetParam,
|
|
13
|
+
GuildListParam,
|
|
14
|
+
GuildMemberGetParam,
|
|
15
|
+
GuildMemberKickParam,
|
|
16
|
+
GuildMemberMuteParam,
|
|
17
|
+
GuildMemberRoleParam,
|
|
18
|
+
GuildRoleCreateParam,
|
|
19
|
+
GuildRoleDeleteParam,
|
|
20
|
+
GuildRoleUpdateParam,
|
|
21
|
+
GuildXXXListParam,
|
|
22
|
+
MessageOpParam,
|
|
23
|
+
MessageParam,
|
|
24
|
+
ReactionCreateParam,
|
|
25
|
+
ReactionDeleteParam,
|
|
26
|
+
ReactionListParam,
|
|
27
|
+
UserChannelCreateParam,
|
|
28
|
+
UserGetParam,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
from .message import QQGroupMessageEncoder, QQGuildMessageEncoder, decode_segments
|
|
32
|
+
from .utils import QQBotNetwork, decode_channel, decode_guild, decode_member, decode_user
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def apply(
|
|
36
|
+
adapter: Adapter, net_getter: Callable[[str, bool], QQBotNetwork], login_getter: Callable[[str, bool], Login]
|
|
37
|
+
):
|
|
38
|
+
@adapter.route(Api.LOGIN_GET)
|
|
39
|
+
async def login_get(request: Request):
|
|
40
|
+
return login_getter(request.self_id, request.platform == "qqguild")
|
|
41
|
+
|
|
42
|
+
@adapter.route(Api.MESSAGE_CREATE)
|
|
43
|
+
async def message_create(request: Request[MessageParam]):
|
|
44
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
45
|
+
login = login_getter(request.self_id, request.platform == "qqguild")
|
|
46
|
+
# encoder = OneBot11MessageEncoder(login, net, request.params["channel_id"])
|
|
47
|
+
if login.platform == "qqguild":
|
|
48
|
+
encoder = QQGuildMessageEncoder(login, net, request.params["channel_id"], request.params.get("referrer"))
|
|
49
|
+
else:
|
|
50
|
+
encoder = QQGroupMessageEncoder(login, net, request.params["channel_id"], request.params.get("referrer"))
|
|
51
|
+
await encoder.send(request.params["content"])
|
|
52
|
+
return encoder.results
|
|
53
|
+
|
|
54
|
+
@adapter.route(Api.USER_CHANNEL_CREATE)
|
|
55
|
+
async def user_channel_create(request: Request[UserChannelCreateParam]):
|
|
56
|
+
if request.platform == "qqguild":
|
|
57
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
58
|
+
if "guild_id" not in request.params:
|
|
59
|
+
raise BadRequestException("guild_id is required for qqguild's user.channel.create")
|
|
60
|
+
guild_id = request.params["guild_id"]
|
|
61
|
+
if "_" in guild_id:
|
|
62
|
+
guild_id = guild_id.split("_")[0]
|
|
63
|
+
res = await net.call_api(
|
|
64
|
+
"post",
|
|
65
|
+
"users/@me/dms",
|
|
66
|
+
{"recipient_id": request.params["user_id"], "source_guild_id": request.params["guild_id"]},
|
|
67
|
+
)
|
|
68
|
+
return Channel(f"{res['guild_id']}_{guild_id}", ChannelType.TEXT)
|
|
69
|
+
else:
|
|
70
|
+
return Channel(f"private:{request.params['user_id']}", ChannelType.DIRECT)
|
|
71
|
+
|
|
72
|
+
@adapter.route(Api.MESSAGE_DELETE)
|
|
73
|
+
async def delete_message(request: Request[MessageOpParam]):
|
|
74
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
75
|
+
if request.platform == "qqguild":
|
|
76
|
+
if "_" in request.params["channel_id"]:
|
|
77
|
+
guild_id = request.params["channel_id"].split("_")[0]
|
|
78
|
+
endpoint = f"dms/{guild_id}/messages/{request.params['message_id']}"
|
|
79
|
+
else:
|
|
80
|
+
endpoint = f"channels/{request.params['channel_id']}/messages/{request.params['message_id']}"
|
|
81
|
+
await net.call_api("delete", endpoint, {"hidetip": "false"})
|
|
82
|
+
else:
|
|
83
|
+
if request.params["channel_id"].startswith("private:"):
|
|
84
|
+
user_id = request.params["channel_id"][8:]
|
|
85
|
+
endpoint = f"v2/users/{user_id}/messages/{request.params['message_id']}"
|
|
86
|
+
else:
|
|
87
|
+
endpoint = f"v2/groups/{request.params['channel_id']}/messages/{request.params['message_id']}"
|
|
88
|
+
await net.call_api("delete", endpoint)
|
|
89
|
+
|
|
90
|
+
@adapter.route(Api.MESSAGE_GET)
|
|
91
|
+
async def get_message(request: Request[MessageOpParam]):
|
|
92
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
93
|
+
if request.platform == "qqguild":
|
|
94
|
+
if "_" in request.params["channel_id"]:
|
|
95
|
+
raise NotFoundException("user-channel is not supported for message.get")
|
|
96
|
+
endpoint = f"channels/{request.params['channel_id']}/messages/{request.params['message_id']}"
|
|
97
|
+
raw = await net.call_api("get", endpoint)
|
|
98
|
+
guild = Guild(raw["guild_id"])
|
|
99
|
+
channel = Channel(raw["channel_id"], ChannelType.TEXT, parent_id=guild.id)
|
|
100
|
+
user = decode_user(raw["author"])
|
|
101
|
+
member = Member(
|
|
102
|
+
user,
|
|
103
|
+
avatar=user.avatar,
|
|
104
|
+
joined_at=datetime.fromisoformat(raw["member"]["joined_at"]),
|
|
105
|
+
)
|
|
106
|
+
msg = decode_segments(raw)
|
|
107
|
+
if len(msg) >= 2 and isinstance(msg[0], At) and isinstance(msg[1], Text):
|
|
108
|
+
text = msg[1].text.lstrip()
|
|
109
|
+
if not text:
|
|
110
|
+
msg.pop(1)
|
|
111
|
+
else:
|
|
112
|
+
msg[1] = Text(text)
|
|
113
|
+
return MessageObject.from_elements(
|
|
114
|
+
raw["id"],
|
|
115
|
+
msg,
|
|
116
|
+
channel=channel,
|
|
117
|
+
guild=guild,
|
|
118
|
+
member=member,
|
|
119
|
+
user=user,
|
|
120
|
+
created_at=(
|
|
121
|
+
datetime.fromtimestamp(int(raw["timestamp"]))
|
|
122
|
+
if isinstance(raw["timestamp"], (int, float)) or raw["timestamp"].isdigit()
|
|
123
|
+
else datetime.fromisoformat(str(raw["timestamp"]))
|
|
124
|
+
),
|
|
125
|
+
referrer={
|
|
126
|
+
"msg_id": raw["id"],
|
|
127
|
+
"msg_seq": -1,
|
|
128
|
+
},
|
|
129
|
+
)
|
|
130
|
+
raise NotFoundException("qq platform does not support message.get")
|
|
131
|
+
|
|
132
|
+
@adapter.route(Api.USER_GET)
|
|
133
|
+
async def guild_get_user(request: Request[UserGetParam]):
|
|
134
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
135
|
+
if request.platform == "qqguild":
|
|
136
|
+
if "_" in request.params["user_id"]:
|
|
137
|
+
guild_id, user_id = request.params["user_id"].split("_", maxsplit=1)
|
|
138
|
+
res = await net.call_api(
|
|
139
|
+
"get",
|
|
140
|
+
f"guilds/{guild_id}/members/{user_id}",
|
|
141
|
+
)
|
|
142
|
+
return decode_user(res)
|
|
143
|
+
raise NotFoundException("qqguild platform does not support user.get without guild_id")
|
|
144
|
+
raise NotFoundException("qq platform does not support user.get")
|
|
145
|
+
|
|
146
|
+
@adapter.route(Api.GUILD_GET)
|
|
147
|
+
async def guild_get(request: Request[GuildGetParam]):
|
|
148
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
149
|
+
if request.platform == "qqguild":
|
|
150
|
+
res = await net.call_api(
|
|
151
|
+
"get",
|
|
152
|
+
f"guilds/{request.params['guild_id']}",
|
|
153
|
+
)
|
|
154
|
+
return decode_guild(res)
|
|
155
|
+
raise NotFoundException("qq platform does not support guild.get")
|
|
156
|
+
|
|
157
|
+
@adapter.route(Api.GUILD_LIST)
|
|
158
|
+
async def guild_list(request: Request[GuildListParam]):
|
|
159
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
160
|
+
if request.platform == "qqguild":
|
|
161
|
+
res = await net.call_api(
|
|
162
|
+
"get",
|
|
163
|
+
"users/@me/guilds",
|
|
164
|
+
{"after": request.params["next"]} if "next" in request.params else None,
|
|
165
|
+
)
|
|
166
|
+
guilds = [decode_guild(guild) for guild in res]
|
|
167
|
+
return PageResult(guilds, next=guilds[-1].id if guilds else None)
|
|
168
|
+
raise NotFoundException("qq platform does not support guild.list")
|
|
169
|
+
|
|
170
|
+
@adapter.route(Api.CHANNEL_GET)
|
|
171
|
+
async def channel_get(request: Request[ChannelParam]):
|
|
172
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
173
|
+
if request.platform == "qqguild":
|
|
174
|
+
channel_id = request.params["channel_id"].split("_")[-1]
|
|
175
|
+
res = await net.call_api(
|
|
176
|
+
"get",
|
|
177
|
+
f"channels/{channel_id}",
|
|
178
|
+
)
|
|
179
|
+
return decode_channel(res)
|
|
180
|
+
raise NotFoundException("qq platform does not support channel.get")
|
|
181
|
+
|
|
182
|
+
@adapter.route(Api.CHANNEL_LIST)
|
|
183
|
+
async def channel_list(request: Request[ChannelListParam]):
|
|
184
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
185
|
+
if request.platform == "qqguild":
|
|
186
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
187
|
+
res = await net.call_api(
|
|
188
|
+
"get",
|
|
189
|
+
f"guilds/{guild_id}/channels",
|
|
190
|
+
)
|
|
191
|
+
channels = [decode_channel(channel) for channel in res]
|
|
192
|
+
return PageResult(channels, next=None)
|
|
193
|
+
raise NotFoundException("qq platform does not support channel.list")
|
|
194
|
+
|
|
195
|
+
@adapter.route(Api.CHANNEL_CREATE)
|
|
196
|
+
async def channel_create(request: Request[ChannelCreateParam]):
|
|
197
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
198
|
+
if request.platform == "qqguild":
|
|
199
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
200
|
+
data = request.params["data"]
|
|
201
|
+
res = await net.call_api(
|
|
202
|
+
"post",
|
|
203
|
+
f"guilds/{guild_id}/channels",
|
|
204
|
+
{
|
|
205
|
+
"name": data["name"],
|
|
206
|
+
"type": {ChannelType.TEXT.value: 0, ChannelType.VOICE.value: 2, ChannelType.CATEGORY.value: 4}.get(
|
|
207
|
+
data.get("type", ChannelType.TEXT.value), 0
|
|
208
|
+
),
|
|
209
|
+
"parent_id": data.get("parent_id"),
|
|
210
|
+
"sub_type": 0,
|
|
211
|
+
"speak_permission": 1,
|
|
212
|
+
},
|
|
213
|
+
)
|
|
214
|
+
return decode_channel(res)
|
|
215
|
+
raise NotFoundException("qq platform does not support channel.create")
|
|
216
|
+
|
|
217
|
+
@adapter.route(Api.CHANNEL_UPDATE)
|
|
218
|
+
async def channel_update(request: Request[ChannelUpdateParam]):
|
|
219
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
220
|
+
if request.platform == "qqguild":
|
|
221
|
+
channel_id = request.params["channel_id"].split("_")[-1]
|
|
222
|
+
data = request.params["data"]
|
|
223
|
+
await net.call_api(
|
|
224
|
+
"patch",
|
|
225
|
+
f"channels/{channel_id}",
|
|
226
|
+
{
|
|
227
|
+
"name": data.get("name"),
|
|
228
|
+
"parent_id": data.get("parent_id"),
|
|
229
|
+
},
|
|
230
|
+
)
|
|
231
|
+
else:
|
|
232
|
+
raise NotFoundException("qq platform does not support channel.update")
|
|
233
|
+
|
|
234
|
+
@adapter.route(Api.CHANNEL_DELETE)
|
|
235
|
+
async def channel_delete(request: Request[ChannelParam]):
|
|
236
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
237
|
+
if request.platform == "qqguild":
|
|
238
|
+
channel_id = request.params["channel_id"].split("_")[-1]
|
|
239
|
+
await net.call_api(
|
|
240
|
+
"delete",
|
|
241
|
+
f"channels/{channel_id}",
|
|
242
|
+
)
|
|
243
|
+
else:
|
|
244
|
+
raise NotFoundException("qq platform does not support channel.delete")
|
|
245
|
+
|
|
246
|
+
@adapter.route(Api.GUILD_MEMBER_GET)
|
|
247
|
+
async def guild_get_member(request: Request[GuildMemberGetParam]):
|
|
248
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
249
|
+
if request.platform == "qqguild":
|
|
250
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
251
|
+
res = await net.call_api(
|
|
252
|
+
"get",
|
|
253
|
+
f"guilds/{guild_id}/members/{request.params['user_id']}",
|
|
254
|
+
)
|
|
255
|
+
return decode_member(res)
|
|
256
|
+
raise NotFoundException("qq platform does not support guild.member.get")
|
|
257
|
+
|
|
258
|
+
@adapter.route(Api.GUILD_MEMBER_LIST)
|
|
259
|
+
async def guild_list_member(request: Request[GuildXXXListParam]):
|
|
260
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
261
|
+
if request.platform == "qqguild":
|
|
262
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
263
|
+
res = await net.call_api(
|
|
264
|
+
"get",
|
|
265
|
+
f"guilds/{guild_id}/members",
|
|
266
|
+
{"limit": 400, "after": request.params["next"] if "next" in request.params else "0"},
|
|
267
|
+
)
|
|
268
|
+
members = [decode_member(member) for member in res]
|
|
269
|
+
return PageResult(members, next=members[-1].user.id if members else None) # type: ignore
|
|
270
|
+
raise NotFoundException("qq platform does not support guild.member.list")
|
|
271
|
+
|
|
272
|
+
@adapter.route(Api.GUILD_MEMBER_KICK)
|
|
273
|
+
async def guild_kick_member(request: Request[GuildMemberKickParam]):
|
|
274
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
275
|
+
if request.platform == "qqguild":
|
|
276
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
277
|
+
await net.call_api(
|
|
278
|
+
"delete",
|
|
279
|
+
f"guilds/{guild_id}/members/{request.params['user_id']}",
|
|
280
|
+
)
|
|
281
|
+
else:
|
|
282
|
+
raise NotFoundException("qq platform does not support guild.member.kick")
|
|
283
|
+
|
|
284
|
+
@adapter.route(Api.GUILD_MEMBER_MUTE)
|
|
285
|
+
async def guild_mute_member(request: Request[GuildMemberMuteParam]):
|
|
286
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
287
|
+
if request.platform == "qqguild":
|
|
288
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
289
|
+
await net.call_api(
|
|
290
|
+
"patch",
|
|
291
|
+
f"guilds/{guild_id}/members/{request.params['user_id']}/mute",
|
|
292
|
+
{"mute_seconds": request.params.get("duration", 0) // 1000},
|
|
293
|
+
)
|
|
294
|
+
else:
|
|
295
|
+
raise NotFoundException("qq platform does not support guild.member.mute")
|
|
296
|
+
|
|
297
|
+
@adapter.route(Api.REACTION_LIST)
|
|
298
|
+
async def reaction_list(request: Request[ReactionListParam]):
|
|
299
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
300
|
+
if request.platform == "qqguild":
|
|
301
|
+
channel_id = request.params["channel_id"].split("_")[-1]
|
|
302
|
+
if ":" in request.params["emoji"]:
|
|
303
|
+
type_, emoji_id = request.params["emoji"].split(":", maxsplit=1)
|
|
304
|
+
else:
|
|
305
|
+
type_, emoji_id = "1", request.params["emoji"]
|
|
306
|
+
res = await net.call_api(
|
|
307
|
+
"get",
|
|
308
|
+
f"channels/{channel_id}/messages/{request.params['message_id']}/reactions/{type_}/{emoji_id}",
|
|
309
|
+
{
|
|
310
|
+
"limit": 50,
|
|
311
|
+
"cookie": request.params.get("next"),
|
|
312
|
+
},
|
|
313
|
+
)
|
|
314
|
+
users = [decode_user(user) for user in res["users"]]
|
|
315
|
+
return PageResult(users, next=None if res["is_end"] else res["cookie"])
|
|
316
|
+
raise NotFoundException("qq platform does not support reaction.list")
|
|
317
|
+
|
|
318
|
+
@adapter.route(Api.REACTION_CREATE)
|
|
319
|
+
async def reaction_create(request: Request[ReactionCreateParam]):
|
|
320
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
321
|
+
if request.platform == "qqguild":
|
|
322
|
+
channel_id = request.params["channel_id"].split("_")[-1]
|
|
323
|
+
if ":" in request.params["emoji"]:
|
|
324
|
+
type_, emoji_id = request.params["emoji"].split(":", maxsplit=1)
|
|
325
|
+
else:
|
|
326
|
+
type_, emoji_id = "1", request.params["emoji"]
|
|
327
|
+
await net.call_api(
|
|
328
|
+
"put",
|
|
329
|
+
f"channels/{channel_id}/messages/{request.params['message_id']}/reactions/{type_}/{emoji_id}/",
|
|
330
|
+
)
|
|
331
|
+
else:
|
|
332
|
+
raise NotFoundException("qq platform does not support reaction.create")
|
|
333
|
+
|
|
334
|
+
@adapter.route(Api.REACTION_DELETE)
|
|
335
|
+
async def reaction_delete(request: Request[ReactionDeleteParam]):
|
|
336
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
337
|
+
if request.platform == "qqguild":
|
|
338
|
+
channel_id = request.params["channel_id"].split("_")[-1]
|
|
339
|
+
if ":" in request.params["emoji"]:
|
|
340
|
+
type_, emoji_id = request.params["emoji"].split(":", maxsplit=1)
|
|
341
|
+
else:
|
|
342
|
+
type_, emoji_id = "1", request.params["emoji"]
|
|
343
|
+
await net.call_api(
|
|
344
|
+
"delete",
|
|
345
|
+
f"channels/{channel_id}/messages/{request.params['message_id']}/reactions/{type_}/{emoji_id}/",
|
|
346
|
+
)
|
|
347
|
+
else:
|
|
348
|
+
raise NotFoundException("qq platform does not support reaction.delete")
|
|
349
|
+
|
|
350
|
+
@adapter.route(Api.GUILD_ROLE_LIST)
|
|
351
|
+
async def guild_list_role(request: Request[GuildXXXListParam]):
|
|
352
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
353
|
+
if request.platform == "qqguild":
|
|
354
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
355
|
+
res = await net.call_api(
|
|
356
|
+
"get",
|
|
357
|
+
f"guilds/{guild_id}/roles",
|
|
358
|
+
)
|
|
359
|
+
roles = [Role(role["id"], role.get("name", "")) for role in res]
|
|
360
|
+
return PageResult(roles, next=None)
|
|
361
|
+
raise NotFoundException("qq platform does not support guild.role.list")
|
|
362
|
+
|
|
363
|
+
@adapter.route(Api.GUILD_ROLE_CREATE)
|
|
364
|
+
async def guild_create_role(request: Request[GuildRoleCreateParam]):
|
|
365
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
366
|
+
if request.platform == "qqguild":
|
|
367
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
368
|
+
data = request.params["role"]
|
|
369
|
+
res = await net.call_api(
|
|
370
|
+
"post",
|
|
371
|
+
f"guilds/{guild_id}/roles",
|
|
372
|
+
{
|
|
373
|
+
"name": data.get("name"),
|
|
374
|
+
"color": data.get("color"),
|
|
375
|
+
"hoist": data.get("hoist"),
|
|
376
|
+
},
|
|
377
|
+
)
|
|
378
|
+
return Role(res["role"]["id"], res["role"]["name"])
|
|
379
|
+
raise NotFoundException("qq platform does not support guild.role.create")
|
|
380
|
+
|
|
381
|
+
@adapter.route(Api.GUILD_ROLE_DELETE)
|
|
382
|
+
async def guild_delete_role(request: Request[GuildRoleDeleteParam]):
|
|
383
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
384
|
+
if request.platform == "qqguild":
|
|
385
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
386
|
+
await net.call_api(
|
|
387
|
+
"delete",
|
|
388
|
+
f"guilds/{guild_id}/roles/{request.params['role_id']}",
|
|
389
|
+
)
|
|
390
|
+
else:
|
|
391
|
+
raise NotFoundException("qq platform does not support guild.role.delete")
|
|
392
|
+
|
|
393
|
+
@adapter.route(Api.GUILD_ROLE_UPDATE)
|
|
394
|
+
async def guild_update_role(request: Request[GuildRoleUpdateParam]):
|
|
395
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
396
|
+
if request.platform == "qqguild":
|
|
397
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
398
|
+
data = request.params["role"]
|
|
399
|
+
await net.call_api(
|
|
400
|
+
"patch",
|
|
401
|
+
f"guilds/{guild_id}/roles/{request.params['role_id']}",
|
|
402
|
+
{
|
|
403
|
+
"name": data.get("name"),
|
|
404
|
+
"color": data.get("color"),
|
|
405
|
+
"hoist": data.get("hoist"),
|
|
406
|
+
},
|
|
407
|
+
)
|
|
408
|
+
else:
|
|
409
|
+
raise NotFoundException("qq platform does not support guild.role.update")
|
|
410
|
+
|
|
411
|
+
@adapter.route(Api.GUILD_MEMBER_ROLE_SET)
|
|
412
|
+
async def guild_add_member_role(request: Request[GuildMemberRoleParam]):
|
|
413
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
414
|
+
if request.platform == "qqguild":
|
|
415
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
416
|
+
await net.call_api(
|
|
417
|
+
"put",
|
|
418
|
+
f"guilds/{guild_id}/members/{request.params['user_id']}/roles/{request.params['role_id']}",
|
|
419
|
+
)
|
|
420
|
+
else:
|
|
421
|
+
raise NotFoundException("qq platform does not support guild.member.role.add")
|
|
422
|
+
|
|
423
|
+
@adapter.route(Api.GUILD_MEMBER_ROLE_UNSET)
|
|
424
|
+
async def guild_remove_member_role(request: Request[GuildMemberRoleParam]):
|
|
425
|
+
net = net_getter(request.self_id, request.platform == "qqguild")
|
|
426
|
+
if request.platform == "qqguild":
|
|
427
|
+
guild_id = request.params["guild_id"].split("_")[0]
|
|
428
|
+
await net.call_api(
|
|
429
|
+
"delete",
|
|
430
|
+
f"guilds/{guild_id}/members/{request.params['user_id']}/roles/{request.params['role_id']}",
|
|
431
|
+
)
|
|
432
|
+
else:
|
|
433
|
+
raise NotFoundException("qq platform does not support guild.member.role.remove")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from weakref import finalize
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class Audit:
|
|
9
|
+
id: str
|
|
10
|
+
guild_id: str
|
|
11
|
+
channel_id: str
|
|
12
|
+
seq: str
|
|
13
|
+
create_time: datetime
|
|
14
|
+
audit_time: datetime
|
|
15
|
+
message_id: str | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AuditResultStore:
|
|
19
|
+
def __init__(self) -> None:
|
|
20
|
+
self._futures: dict[str, asyncio.Future] = {}
|
|
21
|
+
finalize(self, self._futures.clear)
|
|
22
|
+
|
|
23
|
+
def add_result(self, result: dict):
|
|
24
|
+
audit = Audit(
|
|
25
|
+
result["audit_id"],
|
|
26
|
+
result["guild_id"],
|
|
27
|
+
result["channel_id"],
|
|
28
|
+
result["seq_in_channel"],
|
|
29
|
+
datetime.fromisoformat(result["create_time"]),
|
|
30
|
+
datetime.fromisoformat(result["audit_time"]),
|
|
31
|
+
result.get("message_id"),
|
|
32
|
+
)
|
|
33
|
+
if future := self._futures.get(audit.id):
|
|
34
|
+
future.set_result(audit)
|
|
35
|
+
|
|
36
|
+
async def fetch(self, audit: str, timeout: float = 30) -> Audit | None:
|
|
37
|
+
future = asyncio.get_event_loop().create_future()
|
|
38
|
+
self._futures[audit] = future
|
|
39
|
+
try:
|
|
40
|
+
return await asyncio.wait_for(future, timeout)
|
|
41
|
+
except asyncio.TimeoutError:
|
|
42
|
+
return None
|
|
43
|
+
finally:
|
|
44
|
+
del self._futures[audit]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
audit_result = AuditResultStore()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from collections.abc import Awaitable, Callable
|
|
2
|
+
|
|
3
|
+
from satori.model import Event, Login
|
|
4
|
+
|
|
5
|
+
from ..utils import Payload, QQBotNetwork
|
|
6
|
+
|
|
7
|
+
EventHandler = Callable[[Login, Login, QQBotNetwork, Payload], Awaitable[Event | None]]
|
|
8
|
+
|
|
9
|
+
event_handlers: dict[str, EventHandler] = {}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def register_event(event_type: str):
|
|
13
|
+
def wrapper(func: EventHandler):
|
|
14
|
+
event_handlers[event_type] = func
|
|
15
|
+
return func
|
|
16
|
+
|
|
17
|
+
return wrapper
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from satori import EventType
|
|
6
|
+
from satori.model import Event, Guild, User
|
|
7
|
+
|
|
8
|
+
from ..utils import Payload
|
|
9
|
+
from .base import register_event
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@register_event("FRIEND_ADD")
|
|
13
|
+
@register_event("FRIEND_DEL")
|
|
14
|
+
async def friend_event(login, guild_login, net, payload: Payload):
|
|
15
|
+
raw = payload.data
|
|
16
|
+
t = {
|
|
17
|
+
"FRIEND_ADD": EventType.FRIEND_ADDED,
|
|
18
|
+
"FRIEND_DEL": EventType.FRIEND_REMOVED,
|
|
19
|
+
}[payload.type or ""]
|
|
20
|
+
user = User(raw["openid"])
|
|
21
|
+
return Event(
|
|
22
|
+
t,
|
|
23
|
+
(
|
|
24
|
+
datetime.fromtimestamp(int(raw["timestamp"]))
|
|
25
|
+
if isinstance(raw["timestamp"], (int, float)) or raw["timestamp"].isdigit()
|
|
26
|
+
else datetime.fromisoformat(str(raw["timestamp"]))
|
|
27
|
+
),
|
|
28
|
+
login,
|
|
29
|
+
user=user,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@register_event("GROUP_ADD_ROBOT")
|
|
34
|
+
@register_event("GROUP_DEL_ROBOT")
|
|
35
|
+
async def group_event(login, guild_login, net, payload: Payload):
|
|
36
|
+
raw = payload.data
|
|
37
|
+
if "group_openid" in raw:
|
|
38
|
+
guild = Guild(raw["group_openid"])
|
|
39
|
+
else:
|
|
40
|
+
guild = Guild(raw["guild_id"])
|
|
41
|
+
operator = User(raw["op_member_openid"])
|
|
42
|
+
t = {
|
|
43
|
+
"GROUP_ADD_ROBOT": EventType.GUILD_ADDED,
|
|
44
|
+
"GROUP_DEL_ROBOT": EventType.GUILD_REMOVED,
|
|
45
|
+
}[payload.type or ""]
|
|
46
|
+
return Event(
|
|
47
|
+
t,
|
|
48
|
+
(
|
|
49
|
+
datetime.fromtimestamp(int(raw["timestamp"]))
|
|
50
|
+
if isinstance(raw["timestamp"], (int, float)) or raw["timestamp"].isdigit()
|
|
51
|
+
else datetime.fromisoformat(str(raw["timestamp"]))
|
|
52
|
+
),
|
|
53
|
+
login,
|
|
54
|
+
guild=guild,
|
|
55
|
+
operator=operator,
|
|
56
|
+
)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from satori import EventType
|
|
6
|
+
from satori.model import Event, Guild, Member, Role, User
|
|
7
|
+
|
|
8
|
+
from ..utils import Payload, decode_channel, decode_guild, decode_user
|
|
9
|
+
from .base import register_event
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@register_event("CHANNEL_CREATE")
|
|
13
|
+
@register_event("CHANNEL_UPDATE")
|
|
14
|
+
@register_event("CHANNEL_DELETE")
|
|
15
|
+
async def channel_event(login, guild_login, net, payload: Payload):
|
|
16
|
+
raw = payload.data
|
|
17
|
+
guild = Guild(raw["guild_id"])
|
|
18
|
+
channel = decode_channel(raw)
|
|
19
|
+
t = {
|
|
20
|
+
"CHANNEL_CREATE": EventType.CHANNEL_ADDED,
|
|
21
|
+
"CHANNEL_UPDATE": EventType.CHANNEL_UPDATED,
|
|
22
|
+
"CHANNEL_DELETE": EventType.CHANNEL_REMOVED,
|
|
23
|
+
}[payload.type or ""]
|
|
24
|
+
operator = User(raw["op_user_id"])
|
|
25
|
+
return Event(t, datetime.now(), guild_login, channel=channel, guild=guild, operator=operator)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@register_event("GUILD_CREATE")
|
|
29
|
+
@register_event("GUILD_UPDATE")
|
|
30
|
+
@register_event("GUILD_DELETE")
|
|
31
|
+
async def guild_event(login, guild_login, net, payload: Payload):
|
|
32
|
+
raw = payload.data
|
|
33
|
+
guild = decode_guild(raw)
|
|
34
|
+
t = {
|
|
35
|
+
"GUILD_CREATE": EventType.GUILD_ADDED,
|
|
36
|
+
"GUILD_UPDATE": EventType.GUILD_UPDATED,
|
|
37
|
+
"GUILD_DELETE": EventType.GUILD_REMOVED,
|
|
38
|
+
}[payload.type or ""]
|
|
39
|
+
operator = User(raw["op_user_id"])
|
|
40
|
+
return Event(t, datetime.now(), guild_login, guild=guild, operator=operator)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@register_event("GUILD_MEMBER_ADD")
|
|
44
|
+
@register_event("GUILD_MEMBER_UPDATE")
|
|
45
|
+
@register_event("GUILD_MEMBER_DELETE")
|
|
46
|
+
async def guild_member_event(login, guild_login, net, payload: Payload):
|
|
47
|
+
raw = payload.data
|
|
48
|
+
guild = Guild(raw["guild_id"])
|
|
49
|
+
t = {
|
|
50
|
+
"GUILD_MEMBER_ADD": EventType.GUILD_MEMBER_ADDED,
|
|
51
|
+
"GUILD_MEMBER_UPDATE": EventType.GUILD_MEMBER_UPDATED,
|
|
52
|
+
"GUILD_MEMBER_DELETE": EventType.GUILD_MEMBER_REMOVED,
|
|
53
|
+
}[payload.type or ""]
|
|
54
|
+
user = decode_user(raw["user"])
|
|
55
|
+
member = Member(
|
|
56
|
+
user,
|
|
57
|
+
nick=raw["nick"],
|
|
58
|
+
avatar=user.avatar,
|
|
59
|
+
joined_at=datetime.fromisoformat(raw["joined_at"]),
|
|
60
|
+
)
|
|
61
|
+
role = Role(raw["roles"][0])
|
|
62
|
+
operator = User(raw["op_user_id"])
|
|
63
|
+
return Event(t, datetime.now(), guild_login, guild=guild, user=user, member=member, operator=operator, role=role)
|