satori-python-adapter-milky 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.
@@ -0,0 +1,29 @@
1
+ includes = ["src/satori/adapters/milky"]
2
+ raw-dependencies = ["satori-python-server >= 0.17.2"]
3
+
4
+ [project]
5
+ name = "satori-python-adapter-milky"
6
+ version = "0.1.0"
7
+ authors = [
8
+ {name = "RF-Tar-Railt", email = "rf_tar_railt@qq.com"}
9
+ ]
10
+ dependencies = []
11
+ description = "Satori Protocol SDK for python, adapter for Milky"
12
+ license = {text = "MIT"}
13
+ readme = "README.md"
14
+ requires-python = ">=3.10,<4.0"
15
+ classifiers = [
16
+ "Typing :: Typed",
17
+ "Development Status :: 4 - Beta",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3.8",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Operating System :: OS Independent",
25
+ ]
26
+
27
+ [project.urls]
28
+ homepage = "https://github.com/RF-Tar-Railt/satori-python"
29
+ repository = "https://github.com/RF-Tar-Railt/satori-python"
@@ -0,0 +1,4 @@
1
+ from .main import MilkyAdapter as MilkyAdapter
2
+ from .webhook import MilkyWebhookAdapter as MilkyWebhookAdapter
3
+
4
+ __all__ = ["MilkyAdapter", "MilkyWebhookAdapter"]
@@ -0,0 +1,311 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+
5
+ from satori import Api
6
+ from satori.model import Channel, ChannelType, Login, PageDequeResult, PageResult
7
+ from satori.server import Adapter, Request
8
+ from satori.server.route import (
9
+ ApproveParam,
10
+ ChannelListParam,
11
+ ChannelMuteParam,
12
+ ChannelParam,
13
+ ChannelUpdateParam,
14
+ FriendListParam,
15
+ GuildGetParam,
16
+ GuildListParam,
17
+ GuildMemberGetParam,
18
+ GuildMemberKickParam,
19
+ GuildMemberMuteParam,
20
+ GuildXXXListParam,
21
+ MessageListParam,
22
+ MessageOpParam,
23
+ MessageParam,
24
+ ReactionCreateParam,
25
+ ReactionDeleteParam,
26
+ UserChannelCreateParam,
27
+ UserGetParam,
28
+ )
29
+
30
+ from .message import MilkyMessageEncoder, decode_message
31
+ from .utils import (
32
+ MilkyNetwork,
33
+ decode_friend,
34
+ decode_group_channel,
35
+ decode_guild,
36
+ decode_member,
37
+ decode_private_channel,
38
+ decode_user_profile,
39
+ get_scene_and_peer,
40
+ )
41
+
42
+
43
+ def apply(
44
+ adapter: Adapter,
45
+ net_getter: Callable[[str], MilkyNetwork],
46
+ login_getter: Callable[[str], Login],
47
+ ):
48
+ @adapter.route(Api.LOGIN_GET)
49
+ async def login_get(request: Request):
50
+ return login_getter(request.self_id)
51
+
52
+ @adapter.route(Api.MESSAGE_CREATE)
53
+ async def message_create(request: Request[MessageParam]):
54
+ net = net_getter(request.self_id)
55
+ login = login_getter(request.self_id)
56
+ encoder = MilkyMessageEncoder(login, net, request.params["channel_id"])
57
+ return await encoder.send(request.params["content"])
58
+
59
+ @adapter.route(Api.MESSAGE_GET)
60
+ async def message_get(request: Request[MessageOpParam]):
61
+ net = net_getter(request.self_id)
62
+ scene, peer_id = get_scene_and_peer(request.params["channel_id"])
63
+ result = await net.call_api(
64
+ "get_message",
65
+ {
66
+ "message_scene": scene,
67
+ "peer_id": peer_id,
68
+ "message_seq": int(request.params["message_id"]),
69
+ },
70
+ )
71
+ if not result or "message" not in result:
72
+ raise RuntimeError("Failed to get message")
73
+ return await decode_message(net, result["message"])
74
+
75
+ @adapter.route(Api.MESSAGE_DELETE)
76
+ async def message_delete(request: Request[MessageOpParam]):
77
+ net = net_getter(request.self_id)
78
+ scene, peer_id = get_scene_and_peer(request.params["channel_id"])
79
+ message_seq = int(request.params["message_id"])
80
+ if scene == "group":
81
+ await net.call_api("recall_group_message", {"group_id": peer_id, "message_seq": message_seq})
82
+ else:
83
+ await net.call_api("recall_private_message", {"user_id": peer_id, "message_seq": message_seq})
84
+ return
85
+
86
+ @adapter.route(Api.MESSAGE_LIST)
87
+ async def message_list(request: Request[MessageListParam]):
88
+ net = net_getter(request.self_id)
89
+ params = request.params
90
+ direction = params.get("direction", "before")
91
+ if direction != "before":
92
+ raise RuntimeError("Milky adapter only supports direction='before'")
93
+ scene, peer_id = get_scene_and_peer(params["channel_id"])
94
+ result = await net.call_api(
95
+ "get_history_messages",
96
+ {
97
+ "message_scene": scene,
98
+ "peer_id": peer_id,
99
+ "start_message_seq": int(params["next"]) if params.get("next") else None, # type: ignore
100
+ "limit": params.get("limit"),
101
+ },
102
+ )
103
+ if not result:
104
+ return PageDequeResult([])
105
+ messages = [await decode_message(net, item) for item in result.get("messages", [])]
106
+ next_seq = result.get("next_message_seq")
107
+ return PageDequeResult(messages, str(next_seq) if next_seq is not None else None)
108
+
109
+ @adapter.route(Api.CHANNEL_GET)
110
+ async def channel_get(request: Request[ChannelParam]):
111
+ net = net_getter(request.self_id)
112
+ channel_id = request.params["channel_id"]
113
+ scene, peer_id = get_scene_and_peer(channel_id)
114
+ if scene == "group":
115
+ result = await net.call_api("get_group_info", {"group_id": peer_id})
116
+ if not result:
117
+ raise RuntimeError("Failed to get group info")
118
+ return decode_group_channel(result["group"])
119
+ profile = await net.call_api("get_user_profile", {"user_id": peer_id})
120
+ if not profile:
121
+ raise RuntimeError("Failed to get user profile")
122
+ return decode_private_channel(profile, channel_id)
123
+
124
+ @adapter.route(Api.CHANNEL_LIST)
125
+ async def channel_list(request: Request[ChannelListParam]):
126
+ net = net_getter(request.self_id)
127
+ guild_id = int(request.params["guild_id"])
128
+ result = await net.call_api("get_group_info", {"group_id": guild_id})
129
+ if not result:
130
+ raise RuntimeError("Failed to get group info")
131
+ channel = decode_group_channel(result["group"])
132
+ return PageResult([channel])
133
+
134
+ @adapter.route(Api.USER_CHANNEL_CREATE)
135
+ async def user_channel_create(request: Request[UserChannelCreateParam]):
136
+ return Channel(f"private:{request.params['user_id']}", ChannelType.DIRECT)
137
+
138
+ @adapter.route(Api.CHANNEL_UPDATE)
139
+ async def channel_update(request: Request[ChannelUpdateParam]):
140
+ net = net_getter(request.self_id)
141
+ data = request.params["data"]
142
+ channel_id = request.params["channel_id"]
143
+ scene, peer_id = get_scene_and_peer(channel_id)
144
+ if scene != "group":
145
+ raise RuntimeError("Only group channels support update")
146
+ if "name" in data:
147
+ await net.call_api("set_group_name", {"group_id": peer_id, "new_group_name": data["name"]})
148
+ return
149
+
150
+ @adapter.route(Api.CHANNEL_MUTE)
151
+ async def channel_mute(request: Request[ChannelMuteParam]):
152
+ net = net_getter(request.self_id)
153
+ scene, peer_id = get_scene_and_peer(request.params["channel_id"])
154
+ if scene != "group":
155
+ raise RuntimeError("Only group channels support mute")
156
+ await net.call_api(
157
+ "set_group_whole_mute",
158
+ {"group_id": peer_id, "is_mute": request.params["duration"] > 0},
159
+ )
160
+ return
161
+
162
+ @adapter.route(Api.GUILD_GET)
163
+ async def guild_get(request: Request[GuildGetParam]):
164
+ net = net_getter(request.self_id)
165
+ guild_id = int(request.params["guild_id"])
166
+ result = await net.call_api("get_group_info", {"group_id": guild_id})
167
+ if not result:
168
+ raise RuntimeError("Failed to get group info")
169
+ return decode_guild(result["group"])
170
+
171
+ @adapter.route(Api.GUILD_LIST)
172
+ async def guild_list(request: Request[GuildListParam]):
173
+ net = net_getter(request.self_id)
174
+ result = await net.call_api("get_group_list", {})
175
+ groups = [decode_guild(item) for item in result.get("groups", [])] if result else []
176
+ return PageResult(groups)
177
+
178
+ @adapter.route(Api.GUILD_MEMBER_GET)
179
+ async def guild_member_get(request: Request[GuildMemberGetParam]):
180
+ net = net_getter(request.self_id)
181
+ result = await net.call_api(
182
+ "get_group_member_info",
183
+ {"group_id": int(request.params["guild_id"]), "user_id": int(request.params["user_id"])},
184
+ )
185
+ if not result:
186
+ raise RuntimeError("Failed to get group member")
187
+ return decode_member(result["member"])
188
+
189
+ @adapter.route(Api.GUILD_MEMBER_LIST)
190
+ async def guild_member_list(request: Request[GuildXXXListParam]):
191
+ net = net_getter(request.self_id)
192
+ result = await net.call_api("get_group_member_list", {"group_id": int(request.params["guild_id"])})
193
+ members = [decode_member(item) for item in result.get("members", [])] if result else []
194
+ return PageResult(members)
195
+
196
+ @adapter.route(Api.GUILD_MEMBER_KICK)
197
+ async def guild_member_kick(request: Request[GuildMemberKickParam]):
198
+ net = net_getter(request.self_id)
199
+ await net.call_api(
200
+ "kick_group_member",
201
+ {
202
+ "group_id": int(request.params["guild_id"]),
203
+ "user_id": int(request.params["user_id"]),
204
+ "reject_add_request": request.params.get("permanent", False),
205
+ },
206
+ )
207
+ return
208
+
209
+ @adapter.route(Api.GUILD_MEMBER_MUTE)
210
+ async def guild_member_mute(request: Request[GuildMemberMuteParam]):
211
+ net = net_getter(request.self_id)
212
+ await net.call_api(
213
+ "set_group_member_mute",
214
+ {
215
+ "group_id": int(request.params["guild_id"]),
216
+ "user_id": int(request.params["user_id"]),
217
+ "duration": int(request.params["duration"]),
218
+ },
219
+ )
220
+ return
221
+
222
+ @adapter.route(Api.GUILD_MEMBER_APPROVE)
223
+ async def guild_member_approve(request: Request[ApproveParam]):
224
+ net = net_getter(request.self_id)
225
+ message_id = request.params["message_id"]
226
+ notification_seq, notification_type, group_id, is_filtered = message_id.split("|")
227
+ params = {
228
+ "notification_seq": int(notification_seq),
229
+ "notification_type": notification_type,
230
+ "group_id": int(group_id),
231
+ "is_filtered": bool(int(is_filtered)),
232
+ }
233
+ if request.params["approve"]:
234
+ await net.call_api("accept_group_request", params)
235
+ else:
236
+ params["reason"] = request.params.get("comment")
237
+ await net.call_api("reject_group_request", params)
238
+ return
239
+
240
+ @adapter.route(Api.GUILD_APPROVE)
241
+ async def guild_approve(request: Request[ApproveParam]):
242
+ net = net_getter(request.self_id)
243
+ group_id, invitation_seq = request.params["message_id"].split("|")
244
+ payload = {"group_id": int(group_id), "invitation_seq": int(invitation_seq)}
245
+ if request.params["approve"]:
246
+ await net.call_api("accept_group_invitation", payload)
247
+ else:
248
+ await net.call_api("reject_group_invitation", payload)
249
+ return
250
+
251
+ @adapter.route(Api.REACTION_CREATE)
252
+ async def reaction_create(request: Request[ReactionCreateParam]):
253
+ net = net_getter(request.self_id)
254
+ scene, peer_id = get_scene_and_peer(request.params["channel_id"])
255
+ if scene != "group":
256
+ raise RuntimeError("Reactions only supported in group channels")
257
+ await net.call_api(
258
+ "send_group_message_reaction",
259
+ {
260
+ "group_id": peer_id,
261
+ "message_seq": int(request.params["message_id"]),
262
+ "reaction": request.params["emoji"],
263
+ "is_add": True,
264
+ },
265
+ )
266
+ return
267
+
268
+ @adapter.route(Api.REACTION_DELETE)
269
+ async def reaction_delete(request: Request[ReactionDeleteParam]):
270
+ net = net_getter(request.self_id)
271
+ scene, peer_id = get_scene_and_peer(request.params["channel_id"])
272
+ if scene != "group":
273
+ raise RuntimeError("Reactions only supported in group channels")
274
+ await net.call_api(
275
+ "send_group_message_reaction",
276
+ {
277
+ "group_id": peer_id,
278
+ "message_seq": int(request.params["message_id"]),
279
+ "reaction": request.params["emoji"],
280
+ "is_add": False,
281
+ },
282
+ )
283
+ return
284
+
285
+ @adapter.route(Api.USER_GET)
286
+ async def user_get(request: Request[UserGetParam]):
287
+ net = net_getter(request.self_id)
288
+ user_id = request.params["user_id"]
289
+ profile = await net.call_api("get_user_profile", {"user_id": int(user_id)})
290
+ if not profile:
291
+ raise RuntimeError("Failed to get user profile")
292
+ return decode_user_profile(profile, user_id)
293
+
294
+ @adapter.route(Api.FRIEND_LIST)
295
+ async def friend_list(request: Request[FriendListParam]):
296
+ net = net_getter(request.self_id)
297
+ result = await net.call_api("get_friend_list", {})
298
+ friends = [decode_friend(item) for item in result.get("friends", [])] if result else []
299
+ return PageResult(friends)
300
+
301
+ @adapter.route(Api.FRIEND_APPROVE)
302
+ async def friend_approve(request: Request[ApproveParam]):
303
+ net = net_getter(request.self_id)
304
+ initiator_uid, is_filtered = request.params["message_id"].split("|")
305
+ payload = {"initiator_uid": initiator_uid, "is_filtered": bool(int(is_filtered))}
306
+ if request.params["approve"]:
307
+ await net.call_api("accept_friend_request", payload)
308
+ else:
309
+ payload["reason"] = request.params.get("comment")
310
+ await net.call_api("reject_friend_request", payload)
311
+ return
@@ -0,0 +1,7 @@
1
+ # import event handlers to register them
2
+ from . import group as group # noqa: F401
3
+ from . import message as message # noqa: F401
4
+ from . import request as request # noqa: F401
5
+ from .base import event_handlers, register_event
6
+
7
+ __all__ = ["event_handlers", "register_event"]
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Awaitable, Callable
4
+
5
+ from satori.model import Event, Login
6
+
7
+ from ..utils import MilkyNetwork
8
+
9
+ EventHandler = Callable[[Login, MilkyNetwork, dict], Awaitable[Event | None]]
10
+
11
+ event_handlers: dict[str, EventHandler] = {}
12
+
13
+
14
+ def register_event(event_type: str):
15
+ def decorator(func: EventHandler):
16
+ event_handlers[event_type] = func
17
+ return func
18
+
19
+ return decorator
@@ -0,0 +1,74 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+
5
+ from satori import EventType
6
+ from satori.model import Channel, ChannelType, Event, Guild, Member, User
7
+
8
+ from ..utils import group_avatar, user_avatar
9
+ from .base import register_event
10
+
11
+
12
+ @register_event("group_member_increase")
13
+ async def group_member_increase(login, net, raw):
14
+ data = raw["data"]
15
+ guild_id = str(data["group_id"])
16
+ guild = Guild(guild_id, avatar=group_avatar(guild_id))
17
+ channel = Channel(guild_id, ChannelType.TEXT)
18
+ user = User(str(data["user_id"]), avatar=user_avatar(data["user_id"]))
19
+ member = Member(user, avatar=user.avatar)
20
+ operator = None
21
+ if data.get("operator_id"):
22
+ operator = User(str(data["operator_id"]), avatar=user_avatar(data["operator_id"]))
23
+ return Event(
24
+ EventType.GUILD_MEMBER_ADDED,
25
+ datetime.fromtimestamp(raw["time"]),
26
+ login,
27
+ guild=guild,
28
+ channel=channel,
29
+ user=user,
30
+ member=member,
31
+ operator=operator,
32
+ )
33
+
34
+
35
+ @register_event("group_member_decrease")
36
+ async def group_member_decrease(login, net, raw):
37
+ data = raw["data"]
38
+ guild_id = str(data["group_id"])
39
+ guild = Guild(guild_id, avatar=group_avatar(guild_id))
40
+ channel = Channel(guild_id, ChannelType.TEXT)
41
+ user = User(str(data["user_id"]), avatar=user_avatar(data["user_id"]))
42
+ member = Member(user, avatar=user.avatar)
43
+ operator = None
44
+ if data.get("operator_id"):
45
+ operator = User(str(data["operator_id"]), avatar=user_avatar(data["operator_id"]))
46
+ return Event(
47
+ EventType.GUILD_MEMBER_REMOVED,
48
+ datetime.fromtimestamp(raw["time"]),
49
+ login,
50
+ guild=guild,
51
+ channel=channel,
52
+ user=user,
53
+ member=member,
54
+ operator=operator,
55
+ )
56
+
57
+
58
+ @register_event("group_name_change")
59
+ async def group_name_change(login, net, raw):
60
+ data = raw["data"]
61
+ guild_id = str(data["group_id"])
62
+ guild = Guild(guild_id, name=data.get("new_group_name"), avatar=group_avatar(guild_id))
63
+ channel = Channel(guild_id, ChannelType.TEXT, name=data.get("new_group_name"))
64
+ operator = None
65
+ if data.get("operator_id"):
66
+ operator = User(str(data["operator_id"]), avatar=user_avatar(data["operator_id"]))
67
+ return Event(
68
+ EventType.GUILD_UPDATED,
69
+ datetime.fromtimestamp(raw["time"]),
70
+ login,
71
+ guild=guild,
72
+ channel=channel,
73
+ operator=operator,
74
+ )
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+
5
+ from satori import EventType
6
+ from satori.model import Channel, ChannelType, Event, Guild, MessageObject, User
7
+
8
+ from ..message import decode_message
9
+ from ..utils import group_avatar, user_avatar
10
+ from .base import register_event
11
+
12
+
13
+ @register_event("message_receive")
14
+ async def message_receive(login, net, raw):
15
+ message = await decode_message(net, raw["data"])
16
+ return Event(
17
+ EventType.MESSAGE_CREATED,
18
+ datetime.fromtimestamp(raw["time"]),
19
+ login,
20
+ channel=message.channel,
21
+ guild=message.guild,
22
+ member=message.member,
23
+ user=message.user,
24
+ message=message,
25
+ )
26
+
27
+
28
+ @register_event("message_recall")
29
+ async def message_recall(login, net, raw):
30
+ data = raw["data"]
31
+ scene = data["message_scene"]
32
+ peer_id = str(data["peer_id"])
33
+ if scene == "group":
34
+ channel = Channel(peer_id, ChannelType.TEXT)
35
+ guild = Guild(peer_id, avatar=group_avatar(peer_id))
36
+ elif scene == "temp":
37
+ channel = Channel(f"private:temp_{peer_id}", ChannelType.DIRECT)
38
+ guild = None
39
+ else:
40
+ channel = Channel(f"private:{peer_id}", ChannelType.DIRECT)
41
+ guild = None
42
+ user = User(str(data["sender_id"]), avatar=user_avatar(data["sender_id"]))
43
+ operator = User(str(data["operator_id"]), avatar=user_avatar(data["operator_id"]))
44
+ message = MessageObject(str(data["message_seq"]), "", channel=channel, guild=guild, user=user)
45
+ return Event(
46
+ EventType.MESSAGE_DELETED,
47
+ datetime.fromtimestamp(raw["time"]),
48
+ login,
49
+ channel=channel,
50
+ guild=guild,
51
+ user=user,
52
+ operator=operator,
53
+ message=message,
54
+ )
55
+
56
+
57
+ @register_event("group_message_reaction")
58
+ async def group_message_reaction(login, net, raw):
59
+ data = raw["data"]
60
+ guild_id = str(data["group_id"])
61
+ guild = Guild(guild_id, avatar=group_avatar(guild_id))
62
+ channel = Channel(guild_id, ChannelType.TEXT)
63
+ user = User(str(data["user_id"]), avatar=user_avatar(data["user_id"]))
64
+ face_id = data["face_id"]
65
+ message = MessageObject(
66
+ str(data["message_seq"]), f"<milky:face id='{face_id}'>", channel=channel, guild=guild, user=user
67
+ )
68
+ if data["is_add"]:
69
+ event_type = EventType.REACTION_ADDED
70
+ else:
71
+ event_type = EventType.REACTION_REMOVED
72
+ return Event(
73
+ event_type,
74
+ datetime.fromtimestamp(raw["time"]),
75
+ login,
76
+ channel=channel,
77
+ guild=guild,
78
+ user=user,
79
+ message=message,
80
+ )
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+
5
+ from satori import EventType
6
+ from satori.model import Channel, ChannelType, Event, Guild, Member, MessageObject, User
7
+
8
+ from ..utils import group_avatar, user_avatar
9
+ from .base import register_event
10
+
11
+
12
+ @register_event("friend_request")
13
+ async def friend_request(login, net, raw):
14
+ data = raw["data"]
15
+ user = User(str(data["initiator_id"]), avatar=user_avatar(data["initiator_id"]))
16
+ channel = Channel(f"private:{user.id}", ChannelType.DIRECT)
17
+ message_id = f"{data['initiator_uid']}|{1 if data.get('is_filtered') else 0}"
18
+ message = MessageObject(message_id, data.get("comment", ""))
19
+ return Event(
20
+ EventType.FRIEND_REQUEST,
21
+ datetime.fromtimestamp(raw["time"]),
22
+ login,
23
+ user=user,
24
+ channel=channel,
25
+ message=message,
26
+ )
27
+
28
+
29
+ @register_event("group_join_request")
30
+ async def group_join_request(login, net, raw):
31
+ data = raw["data"]
32
+ guild_id = str(data["group_id"])
33
+ guild = Guild(guild_id, avatar=group_avatar(guild_id))
34
+ channel = Channel(guild_id)
35
+ user = User(str(data["initiator_id"]), avatar=user_avatar(data["initiator_id"]))
36
+ member = Member(user, avatar=user_avatar(data["initiator_id"]))
37
+ message_id = f"{data['notification_seq']}|join_request|{guild_id}|{1 if data.get('is_filtered') else 0}"
38
+ message = MessageObject(message_id, data.get("comment", ""))
39
+ return Event(
40
+ EventType.GUILD_MEMBER_REQUEST,
41
+ datetime.fromtimestamp(raw["time"]),
42
+ login,
43
+ guild=guild,
44
+ channel=channel,
45
+ user=user,
46
+ member=member,
47
+ message=message,
48
+ )
49
+
50
+
51
+ @register_event("group_invited_join_request")
52
+ async def group_invited_join_request(login, net, raw):
53
+ data = raw["data"]
54
+ guild_id = str(data["group_id"])
55
+ guild = Guild(guild_id, avatar=group_avatar(guild_id))
56
+ channel = Channel(guild_id)
57
+ user = User(str(data["target_user_id"]), avatar=user_avatar(data["target_user_id"]))
58
+ member = Member(user, avatar=user.avatar)
59
+ message_id = f"{data['notification_seq']}|invited_join_request|{guild_id}|0"
60
+ message = MessageObject(message_id, "")
61
+ operator = User(str(data["initiator_id"]), avatar=user_avatar(data["initiator_id"]))
62
+ return Event(
63
+ EventType.GUILD_MEMBER_REQUEST,
64
+ datetime.fromtimestamp(raw["time"]),
65
+ login,
66
+ guild=guild,
67
+ channel=channel,
68
+ user=user,
69
+ member=member,
70
+ operator=operator,
71
+ message=message,
72
+ )
73
+
74
+
75
+ @register_event("group_invitation")
76
+ async def group_invitation(login, net, raw):
77
+ data = raw["data"]
78
+ guild_id = str(data["group_id"])
79
+ guild = Guild(guild_id, avatar=group_avatar(guild_id))
80
+ channel = Channel(guild_id)
81
+ user = User(str(data["initiator_id"]), avatar=user_avatar(data["initiator_id"]))
82
+ message_id = f"{guild_id}|{data['invitation_seq']}"
83
+ message = MessageObject(message_id, "")
84
+ return Event(
85
+ EventType.GUILD_REQUEST,
86
+ datetime.fromtimestamp(raw["time"]),
87
+ login,
88
+ guild=guild,
89
+ channel=channel,
90
+ user=user,
91
+ message=message,
92
+ )