loopbot-discord-sdk 1.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.
- loopbot/__init__.py +60 -0
- loopbot/bot.py +584 -0
- loopbot/builders/__init__.py +30 -0
- loopbot/builders/action_row.py +40 -0
- loopbot/builders/button.py +58 -0
- loopbot/builders/container.py +61 -0
- loopbot/builders/embed.py +95 -0
- loopbot/builders/file.py +33 -0
- loopbot/builders/media_gallery.py +36 -0
- loopbot/builders/modal.py +67 -0
- loopbot/builders/section.py +52 -0
- loopbot/builders/select_menu.py +63 -0
- loopbot/builders/separator.py +31 -0
- loopbot/builders/text_display.py +24 -0
- loopbot/client.py +805 -0
- loopbot/context/__init__.py +17 -0
- loopbot/context/base.py +263 -0
- loopbot/context/button.py +28 -0
- loopbot/context/command.py +93 -0
- loopbot/context/modal.py +46 -0
- loopbot/context/select.py +34 -0
- loopbot/types.py +76 -0
- loopbot_discord_sdk-1.0.0.dist-info/METADATA +434 -0
- loopbot_discord_sdk-1.0.0.dist-info/RECORD +26 -0
- loopbot_discord_sdk-1.0.0.dist-info/WHEEL +5 -0
- loopbot_discord_sdk-1.0.0.dist-info/top_level.txt +1 -0
loopbot/client.py
ADDED
|
@@ -0,0 +1,805 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTTP/SSE Client for Loop Discord SDK
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
import aiohttp
|
|
10
|
+
from aiohttp_sse_client import client as sse_client
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Client:
|
|
14
|
+
"""HTTP client for communicating with the Loop API"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, token: str, base_url: str = "https://gatewayloop.discloud.app"):
|
|
17
|
+
self.token = token
|
|
18
|
+
self.base_url = base_url.rstrip("/")
|
|
19
|
+
self._session: Optional[aiohttp.ClientSession] = None
|
|
20
|
+
self._sse_task: Optional[asyncio.Task] = None
|
|
21
|
+
self._connected = False
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def headers(self) -> Dict[str, str]:
|
|
25
|
+
return {
|
|
26
|
+
"Authorization": f"Bearer {self.token}",
|
|
27
|
+
"Content-Type": "application/json",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async def _get_session(self) -> aiohttp.ClientSession:
|
|
31
|
+
if self._session is None or self._session.closed:
|
|
32
|
+
self._session = aiohttp.ClientSession()
|
|
33
|
+
return self._session
|
|
34
|
+
|
|
35
|
+
async def request(
|
|
36
|
+
self,
|
|
37
|
+
method: str,
|
|
38
|
+
endpoint: str,
|
|
39
|
+
body: Optional[Dict[str, Any]] = None,
|
|
40
|
+
) -> Dict[str, Any]:
|
|
41
|
+
"""Make an HTTP request to the API"""
|
|
42
|
+
session = await self._get_session()
|
|
43
|
+
url = f"{self.base_url}/api{endpoint}"
|
|
44
|
+
|
|
45
|
+
async with session.request(
|
|
46
|
+
method,
|
|
47
|
+
url,
|
|
48
|
+
headers=self.headers,
|
|
49
|
+
json=body,
|
|
50
|
+
) as response:
|
|
51
|
+
if not response.ok:
|
|
52
|
+
error_text = await response.text()
|
|
53
|
+
raise Exception(f"API error {response.status}: {error_text}")
|
|
54
|
+
|
|
55
|
+
if response.status == 204:
|
|
56
|
+
return {}
|
|
57
|
+
|
|
58
|
+
return await response.json()
|
|
59
|
+
|
|
60
|
+
async def connect(self, commands: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
61
|
+
"""Connect to the API and deploy commands"""
|
|
62
|
+
result = await self.request("POST", "/sdk/connect", {"commands": commands})
|
|
63
|
+
self._connected = True
|
|
64
|
+
return result
|
|
65
|
+
|
|
66
|
+
async def connect_sse(self, on_interaction: Callable[[Dict[str, Any]], None]) -> None:
|
|
67
|
+
"""Connect to SSE for receiving interactions"""
|
|
68
|
+
url = f"{self.base_url}/api/sdk/events"
|
|
69
|
+
|
|
70
|
+
async with sse_client.EventSource(
|
|
71
|
+
url,
|
|
72
|
+
headers=self.headers,
|
|
73
|
+
) as event_source:
|
|
74
|
+
async for event in event_source:
|
|
75
|
+
if event.type == "connected":
|
|
76
|
+
print("[Loop SDK] SSE connected")
|
|
77
|
+
elif event.type == "interaction":
|
|
78
|
+
try:
|
|
79
|
+
interaction = json.loads(event.data)
|
|
80
|
+
on_interaction(interaction)
|
|
81
|
+
except json.JSONDecodeError as e:
|
|
82
|
+
print(f"[Loop SDK] Failed to parse interaction: {e}")
|
|
83
|
+
elif event.type == "ping":
|
|
84
|
+
pass # Heartbeat
|
|
85
|
+
|
|
86
|
+
async def respond(self, interaction_id: str, response: Dict[str, Any]) -> None:
|
|
87
|
+
"""Send interaction response"""
|
|
88
|
+
await self.request("POST", "/sdk/respond", {
|
|
89
|
+
"interactionId": interaction_id,
|
|
90
|
+
"response": response,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
async def follow_up(
|
|
94
|
+
self,
|
|
95
|
+
application_id: str,
|
|
96
|
+
interaction_token: str,
|
|
97
|
+
data: Dict[str, Any],
|
|
98
|
+
) -> Dict[str, Any]:
|
|
99
|
+
"""Send follow-up message"""
|
|
100
|
+
return await self.request("POST", "/sdk/followup", {
|
|
101
|
+
"applicationId": application_id,
|
|
102
|
+
"interactionToken": interaction_token,
|
|
103
|
+
"data": data,
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
async def edit_original(
|
|
107
|
+
self,
|
|
108
|
+
application_id: str,
|
|
109
|
+
interaction_token: str,
|
|
110
|
+
data: Dict[str, Any],
|
|
111
|
+
) -> None:
|
|
112
|
+
"""Edit original response"""
|
|
113
|
+
await self.request("POST", "/sdk/edit", {
|
|
114
|
+
"applicationId": application_id,
|
|
115
|
+
"interactionToken": interaction_token,
|
|
116
|
+
"data": data,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
async def disconnect(self) -> None:
|
|
120
|
+
"""Disconnect from API"""
|
|
121
|
+
if self._connected:
|
|
122
|
+
try:
|
|
123
|
+
await self.request("POST", "/sdk/disconnect", {})
|
|
124
|
+
except Exception:
|
|
125
|
+
pass
|
|
126
|
+
self._connected = False
|
|
127
|
+
|
|
128
|
+
if self._session and not self._session.closed:
|
|
129
|
+
await self._session.close()
|
|
130
|
+
self._session = None
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def is_connected(self) -> bool:
|
|
134
|
+
return self._connected
|
|
135
|
+
|
|
136
|
+
# ==================== MESSAGING ====================
|
|
137
|
+
|
|
138
|
+
async def send_message(
|
|
139
|
+
self,
|
|
140
|
+
application_id: str,
|
|
141
|
+
channel_id: str,
|
|
142
|
+
content: Optional[str] = None,
|
|
143
|
+
embeds: Optional[List[Dict[str, Any]]] = None,
|
|
144
|
+
components: Optional[List[Dict[str, Any]]] = None,
|
|
145
|
+
) -> Dict[str, Any]:
|
|
146
|
+
"""Send a message to a channel"""
|
|
147
|
+
data = {"applicationId": application_id, "channelId": channel_id}
|
|
148
|
+
if content:
|
|
149
|
+
data["content"] = content
|
|
150
|
+
if embeds:
|
|
151
|
+
data["embeds"] = embeds
|
|
152
|
+
if components:
|
|
153
|
+
data["components"] = components
|
|
154
|
+
result = await self.request("POST", "/sdk/messages/send", data)
|
|
155
|
+
return result.get("message", {})
|
|
156
|
+
|
|
157
|
+
async def edit_message(
|
|
158
|
+
self,
|
|
159
|
+
application_id: str,
|
|
160
|
+
channel_id: str,
|
|
161
|
+
message_id: str,
|
|
162
|
+
content: Optional[str] = None,
|
|
163
|
+
embeds: Optional[List[Dict[str, Any]]] = None,
|
|
164
|
+
components: Optional[List[Dict[str, Any]]] = None,
|
|
165
|
+
) -> Dict[str, Any]:
|
|
166
|
+
"""Edit a message"""
|
|
167
|
+
data = {"applicationId": application_id, "channelId": channel_id, "messageId": message_id}
|
|
168
|
+
if content is not None:
|
|
169
|
+
data["content"] = content
|
|
170
|
+
if embeds:
|
|
171
|
+
data["embeds"] = embeds
|
|
172
|
+
if components:
|
|
173
|
+
data["components"] = components
|
|
174
|
+
result = await self.request("POST", "/sdk/messages/edit", data)
|
|
175
|
+
return result.get("message", {})
|
|
176
|
+
|
|
177
|
+
async def delete_message(
|
|
178
|
+
self,
|
|
179
|
+
application_id: str,
|
|
180
|
+
channel_id: str,
|
|
181
|
+
message_id: str,
|
|
182
|
+
) -> None:
|
|
183
|
+
"""Delete a message"""
|
|
184
|
+
await self.request("POST", "/sdk/messages/delete", {
|
|
185
|
+
"applicationId": application_id,
|
|
186
|
+
"channelId": channel_id,
|
|
187
|
+
"messageId": message_id,
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
async def get_messages(
|
|
191
|
+
self,
|
|
192
|
+
application_id: str,
|
|
193
|
+
channel_id: str,
|
|
194
|
+
limit: Optional[int] = None,
|
|
195
|
+
before: Optional[str] = None,
|
|
196
|
+
after: Optional[str] = None,
|
|
197
|
+
) -> List[Dict[str, Any]]:
|
|
198
|
+
"""Get messages from a channel"""
|
|
199
|
+
params = []
|
|
200
|
+
if limit:
|
|
201
|
+
params.append(f"limit={limit}")
|
|
202
|
+
if before:
|
|
203
|
+
params.append(f"before={before}")
|
|
204
|
+
if after:
|
|
205
|
+
params.append(f"after={after}")
|
|
206
|
+
endpoint = f"/sdk/messages/{application_id}/{channel_id}"
|
|
207
|
+
if params:
|
|
208
|
+
endpoint += "?" + "&".join(params)
|
|
209
|
+
result = await self.request("GET", endpoint)
|
|
210
|
+
return result.get("messages", [])
|
|
211
|
+
|
|
212
|
+
async def get_message(
|
|
213
|
+
self,
|
|
214
|
+
application_id: str,
|
|
215
|
+
channel_id: str,
|
|
216
|
+
message_id: str,
|
|
217
|
+
) -> Dict[str, Any]:
|
|
218
|
+
"""Get a single message"""
|
|
219
|
+
result = await self.request("GET", f"/sdk/messages/{application_id}/{channel_id}/{message_id}")
|
|
220
|
+
return result.get("message", {})
|
|
221
|
+
|
|
222
|
+
# ==================== CHANNELS ====================
|
|
223
|
+
|
|
224
|
+
async def get_channel(self, application_id: str, channel_id: str) -> Dict[str, Any]:
|
|
225
|
+
"""Get a channel"""
|
|
226
|
+
result = await self.request("GET", f"/sdk/channels/{application_id}/{channel_id}")
|
|
227
|
+
return result.get("channel", {})
|
|
228
|
+
|
|
229
|
+
async def create_channel(
|
|
230
|
+
self,
|
|
231
|
+
application_id: str,
|
|
232
|
+
guild_id: str,
|
|
233
|
+
name: str,
|
|
234
|
+
channel_type: Optional[int] = None,
|
|
235
|
+
topic: Optional[str] = None,
|
|
236
|
+
permission_overwrites: Optional[List[Dict[str, Any]]] = None,
|
|
237
|
+
parent_id: Optional[str] = None,
|
|
238
|
+
) -> Dict[str, Any]:
|
|
239
|
+
"""Create a channel in a guild"""
|
|
240
|
+
data: Dict[str, Any] = {
|
|
241
|
+
"applicationId": application_id,
|
|
242
|
+
"guildId": guild_id,
|
|
243
|
+
"name": name,
|
|
244
|
+
}
|
|
245
|
+
if channel_type is not None:
|
|
246
|
+
data["type"] = channel_type
|
|
247
|
+
if topic:
|
|
248
|
+
data["topic"] = topic
|
|
249
|
+
if permission_overwrites:
|
|
250
|
+
data["permission_overwrites"] = permission_overwrites
|
|
251
|
+
if parent_id:
|
|
252
|
+
data["parent_id"] = parent_id
|
|
253
|
+
result = await self.request("POST", "/sdk/channels/create", data)
|
|
254
|
+
return result.get("channel", {})
|
|
255
|
+
|
|
256
|
+
async def modify_channel(
|
|
257
|
+
self,
|
|
258
|
+
application_id: str,
|
|
259
|
+
channel_id: str,
|
|
260
|
+
name: Optional[str] = None,
|
|
261
|
+
topic: Optional[str] = None,
|
|
262
|
+
permission_overwrites: Optional[List[Dict[str, Any]]] = None,
|
|
263
|
+
) -> Dict[str, Any]:
|
|
264
|
+
"""Modify a channel"""
|
|
265
|
+
data: Dict[str, Any] = {"applicationId": application_id, "channelId": channel_id}
|
|
266
|
+
if name:
|
|
267
|
+
data["name"] = name
|
|
268
|
+
if topic is not None:
|
|
269
|
+
data["topic"] = topic
|
|
270
|
+
if permission_overwrites:
|
|
271
|
+
data["permission_overwrites"] = permission_overwrites
|
|
272
|
+
result = await self.request("POST", "/sdk/channels/modify", data)
|
|
273
|
+
return result.get("channel", {})
|
|
274
|
+
|
|
275
|
+
async def delete_channel(self, application_id: str, channel_id: str) -> None:
|
|
276
|
+
"""Delete a channel"""
|
|
277
|
+
await self.request("POST", "/sdk/channels/delete", {
|
|
278
|
+
"applicationId": application_id,
|
|
279
|
+
"channelId": channel_id,
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
# ==================== GUILDS ====================
|
|
283
|
+
|
|
284
|
+
async def get_guild(self, application_id: str, guild_id: str) -> Dict[str, Any]:
|
|
285
|
+
"""Get guild info"""
|
|
286
|
+
result = await self.request("GET", f"/sdk/guilds/{application_id}/{guild_id}")
|
|
287
|
+
return result.get("guild", {})
|
|
288
|
+
|
|
289
|
+
async def get_guild_channels(self, application_id: str, guild_id: str) -> List[Dict[str, Any]]:
|
|
290
|
+
"""Get guild channels"""
|
|
291
|
+
result = await self.request("GET", f"/sdk/guilds/{application_id}/{guild_id}/channels")
|
|
292
|
+
return result.get("channels", [])
|
|
293
|
+
|
|
294
|
+
async def get_guild_roles(self, application_id: str, guild_id: str) -> List[Dict[str, Any]]:
|
|
295
|
+
"""Get guild roles"""
|
|
296
|
+
result = await self.request("GET", f"/sdk/guilds/{application_id}/{guild_id}/roles")
|
|
297
|
+
return result.get("roles", [])
|
|
298
|
+
|
|
299
|
+
# ==================== MEMBERS ====================
|
|
300
|
+
|
|
301
|
+
async def get_guild_member(
|
|
302
|
+
self,
|
|
303
|
+
application_id: str,
|
|
304
|
+
guild_id: str,
|
|
305
|
+
user_id: str,
|
|
306
|
+
) -> Dict[str, Any]:
|
|
307
|
+
"""Get a guild member"""
|
|
308
|
+
result = await self.request("GET", f"/sdk/members/{application_id}/{guild_id}/{user_id}")
|
|
309
|
+
return result.get("member", {})
|
|
310
|
+
|
|
311
|
+
async def list_guild_members(
|
|
312
|
+
self,
|
|
313
|
+
application_id: str,
|
|
314
|
+
guild_id: str,
|
|
315
|
+
limit: Optional[int] = None,
|
|
316
|
+
after: Optional[str] = None,
|
|
317
|
+
) -> List[Dict[str, Any]]:
|
|
318
|
+
"""List guild members"""
|
|
319
|
+
params = []
|
|
320
|
+
if limit:
|
|
321
|
+
params.append(f"limit={limit}")
|
|
322
|
+
if after:
|
|
323
|
+
params.append(f"after={after}")
|
|
324
|
+
endpoint = f"/sdk/members/{application_id}/{guild_id}"
|
|
325
|
+
if params:
|
|
326
|
+
endpoint += "?" + "&".join(params)
|
|
327
|
+
result = await self.request("GET", endpoint)
|
|
328
|
+
return result.get("members", [])
|
|
329
|
+
|
|
330
|
+
async def add_member_role(
|
|
331
|
+
self,
|
|
332
|
+
application_id: str,
|
|
333
|
+
guild_id: str,
|
|
334
|
+
user_id: str,
|
|
335
|
+
role_id: str,
|
|
336
|
+
) -> None:
|
|
337
|
+
"""Add role to member"""
|
|
338
|
+
await self.request("POST", "/sdk/members/roles/add", {
|
|
339
|
+
"applicationId": application_id,
|
|
340
|
+
"guildId": guild_id,
|
|
341
|
+
"userId": user_id,
|
|
342
|
+
"roleId": role_id,
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
async def remove_member_role(
|
|
346
|
+
self,
|
|
347
|
+
application_id: str,
|
|
348
|
+
guild_id: str,
|
|
349
|
+
user_id: str,
|
|
350
|
+
role_id: str,
|
|
351
|
+
) -> None:
|
|
352
|
+
"""Remove role from member"""
|
|
353
|
+
await self.request("POST", "/sdk/members/roles/remove", {
|
|
354
|
+
"applicationId": application_id,
|
|
355
|
+
"guildId": guild_id,
|
|
356
|
+
"userId": user_id,
|
|
357
|
+
"roleId": role_id,
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
async def kick_member(
|
|
361
|
+
self,
|
|
362
|
+
application_id: str,
|
|
363
|
+
guild_id: str,
|
|
364
|
+
user_id: str,
|
|
365
|
+
) -> None:
|
|
366
|
+
"""Kick member"""
|
|
367
|
+
await self.request("POST", "/sdk/members/kick", {
|
|
368
|
+
"applicationId": application_id,
|
|
369
|
+
"guildId": guild_id,
|
|
370
|
+
"userId": user_id,
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
async def ban_member(
|
|
374
|
+
self,
|
|
375
|
+
application_id: str,
|
|
376
|
+
guild_id: str,
|
|
377
|
+
user_id: str,
|
|
378
|
+
delete_message_seconds: Optional[int] = None,
|
|
379
|
+
) -> None:
|
|
380
|
+
"""Ban member"""
|
|
381
|
+
data: Dict[str, Any] = {
|
|
382
|
+
"applicationId": application_id,
|
|
383
|
+
"guildId": guild_id,
|
|
384
|
+
"userId": user_id,
|
|
385
|
+
}
|
|
386
|
+
if delete_message_seconds:
|
|
387
|
+
data["deleteMessageSeconds"] = delete_message_seconds
|
|
388
|
+
await self.request("POST", "/sdk/members/ban", data)
|
|
389
|
+
|
|
390
|
+
async def unban_member(
|
|
391
|
+
self,
|
|
392
|
+
application_id: str,
|
|
393
|
+
guild_id: str,
|
|
394
|
+
user_id: str,
|
|
395
|
+
) -> None:
|
|
396
|
+
"""Unban member"""
|
|
397
|
+
await self.request("POST", "/sdk/members/unban", {
|
|
398
|
+
"applicationId": application_id,
|
|
399
|
+
"guildId": guild_id,
|
|
400
|
+
"userId": user_id,
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
# ==================== REACTIONS ====================
|
|
404
|
+
|
|
405
|
+
async def add_reaction(
|
|
406
|
+
self,
|
|
407
|
+
application_id: str,
|
|
408
|
+
channel_id: str,
|
|
409
|
+
message_id: str,
|
|
410
|
+
emoji: str,
|
|
411
|
+
) -> None:
|
|
412
|
+
"""Add reaction to message"""
|
|
413
|
+
await self.request("POST", "/sdk/reactions/add", {
|
|
414
|
+
"applicationId": application_id,
|
|
415
|
+
"channelId": channel_id,
|
|
416
|
+
"messageId": message_id,
|
|
417
|
+
"emoji": emoji,
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
async def remove_reaction(
|
|
421
|
+
self,
|
|
422
|
+
application_id: str,
|
|
423
|
+
channel_id: str,
|
|
424
|
+
message_id: str,
|
|
425
|
+
emoji: str,
|
|
426
|
+
) -> None:
|
|
427
|
+
"""Remove reaction from message"""
|
|
428
|
+
await self.request("POST", "/sdk/reactions/remove", {
|
|
429
|
+
"applicationId": application_id,
|
|
430
|
+
"channelId": channel_id,
|
|
431
|
+
"messageId": message_id,
|
|
432
|
+
"emoji": emoji,
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
# ==================== PINS ====================
|
|
436
|
+
|
|
437
|
+
async def pin_message(
|
|
438
|
+
self,
|
|
439
|
+
application_id: str,
|
|
440
|
+
channel_id: str,
|
|
441
|
+
message_id: str,
|
|
442
|
+
) -> None:
|
|
443
|
+
"""Pin a message"""
|
|
444
|
+
await self.request("POST", "/sdk/pins/add", {
|
|
445
|
+
"applicationId": application_id,
|
|
446
|
+
"channelId": channel_id,
|
|
447
|
+
"messageId": message_id,
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
async def unpin_message(
|
|
451
|
+
self,
|
|
452
|
+
application_id: str,
|
|
453
|
+
channel_id: str,
|
|
454
|
+
message_id: str,
|
|
455
|
+
) -> None:
|
|
456
|
+
"""Unpin a message"""
|
|
457
|
+
await self.request("POST", "/sdk/pins/remove", {
|
|
458
|
+
"applicationId": application_id,
|
|
459
|
+
"channelId": channel_id,
|
|
460
|
+
"messageId": message_id,
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
async def get_pinned_messages(
|
|
464
|
+
self,
|
|
465
|
+
application_id: str,
|
|
466
|
+
channel_id: str,
|
|
467
|
+
) -> List[Dict[str, Any]]:
|
|
468
|
+
"""Get pinned messages"""
|
|
469
|
+
result = await self.request("GET", f"/sdk/pins/{application_id}/{channel_id}")
|
|
470
|
+
return result.get("messages", [])
|
|
471
|
+
|
|
472
|
+
# ==================== USERS ====================
|
|
473
|
+
|
|
474
|
+
async def get_user(self, application_id: str, user_id: str) -> Dict[str, Any]:
|
|
475
|
+
"""Get user info"""
|
|
476
|
+
result = await self.request("GET", f"/sdk/users/{application_id}/{user_id}")
|
|
477
|
+
return result.get("user", {})
|
|
478
|
+
|
|
479
|
+
# ==================== THREADS ====================
|
|
480
|
+
|
|
481
|
+
async def create_thread(
|
|
482
|
+
self,
|
|
483
|
+
application_id: str,
|
|
484
|
+
channel_id: str,
|
|
485
|
+
name: str,
|
|
486
|
+
message_id: Optional[str] = None,
|
|
487
|
+
thread_type: Optional[int] = None,
|
|
488
|
+
auto_archive_duration: Optional[int] = None,
|
|
489
|
+
) -> Dict[str, Any]:
|
|
490
|
+
"""Create a thread"""
|
|
491
|
+
data: Dict[str, Any] = {
|
|
492
|
+
"applicationId": application_id,
|
|
493
|
+
"channelId": channel_id,
|
|
494
|
+
"name": name,
|
|
495
|
+
}
|
|
496
|
+
if message_id:
|
|
497
|
+
data["messageId"] = message_id
|
|
498
|
+
if thread_type:
|
|
499
|
+
data["type"] = thread_type
|
|
500
|
+
if auto_archive_duration:
|
|
501
|
+
data["auto_archive_duration"] = auto_archive_duration
|
|
502
|
+
result = await self.request("POST", "/sdk/threads/create", data)
|
|
503
|
+
return result.get("thread", {})
|
|
504
|
+
|
|
505
|
+
# ==================== FORUM CHANNELS ====================
|
|
506
|
+
|
|
507
|
+
async def create_forum_post(
|
|
508
|
+
self,
|
|
509
|
+
application_id: str,
|
|
510
|
+
channel_id: str,
|
|
511
|
+
name: str,
|
|
512
|
+
message: Dict[str, Any],
|
|
513
|
+
applied_tags: Optional[List[str]] = None,
|
|
514
|
+
auto_archive_duration: Optional[int] = None,
|
|
515
|
+
) -> Dict[str, Any]:
|
|
516
|
+
"""Create a forum post"""
|
|
517
|
+
data: Dict[str, Any] = {
|
|
518
|
+
"applicationId": application_id,
|
|
519
|
+
"channelId": channel_id,
|
|
520
|
+
"name": name,
|
|
521
|
+
"message": message,
|
|
522
|
+
}
|
|
523
|
+
if applied_tags:
|
|
524
|
+
data["applied_tags"] = applied_tags
|
|
525
|
+
if auto_archive_duration:
|
|
526
|
+
data["auto_archive_duration"] = auto_archive_duration
|
|
527
|
+
result = await self.request("POST", "/sdk/forum/post", data)
|
|
528
|
+
return result.get("post", {})
|
|
529
|
+
|
|
530
|
+
async def get_forum_tags(
|
|
531
|
+
self, application_id: str, channel_id: str
|
|
532
|
+
) -> List[Dict[str, Any]]:
|
|
533
|
+
"""Get forum tags"""
|
|
534
|
+
result = await self.request("GET", f"/sdk/forum/{application_id}/{channel_id}/tags")
|
|
535
|
+
return result.get("tags", [])
|
|
536
|
+
|
|
537
|
+
async def modify_forum_tags(
|
|
538
|
+
self,
|
|
539
|
+
application_id: str,
|
|
540
|
+
channel_id: str,
|
|
541
|
+
tags: List[Dict[str, Any]],
|
|
542
|
+
) -> Dict[str, Any]:
|
|
543
|
+
"""Modify forum tags"""
|
|
544
|
+
result = await self.request("POST", "/sdk/forum/tags/modify", {
|
|
545
|
+
"applicationId": application_id,
|
|
546
|
+
"channelId": channel_id,
|
|
547
|
+
"tags": tags,
|
|
548
|
+
})
|
|
549
|
+
return result.get("channel", {})
|
|
550
|
+
|
|
551
|
+
async def archive_thread(
|
|
552
|
+
self,
|
|
553
|
+
application_id: str,
|
|
554
|
+
thread_id: str,
|
|
555
|
+
archived: bool = True,
|
|
556
|
+
) -> Dict[str, Any]:
|
|
557
|
+
"""Archive thread"""
|
|
558
|
+
result = await self.request("POST", "/sdk/forum/archive", {
|
|
559
|
+
"applicationId": application_id,
|
|
560
|
+
"threadId": thread_id,
|
|
561
|
+
"archived": archived,
|
|
562
|
+
})
|
|
563
|
+
return result.get("thread", {})
|
|
564
|
+
|
|
565
|
+
async def lock_thread(
|
|
566
|
+
self,
|
|
567
|
+
application_id: str,
|
|
568
|
+
thread_id: str,
|
|
569
|
+
locked: bool = True,
|
|
570
|
+
) -> Dict[str, Any]:
|
|
571
|
+
"""Lock thread"""
|
|
572
|
+
result = await self.request("POST", "/sdk/forum/lock", {
|
|
573
|
+
"applicationId": application_id,
|
|
574
|
+
"threadId": thread_id,
|
|
575
|
+
"locked": locked,
|
|
576
|
+
})
|
|
577
|
+
return result.get("thread", {})
|
|
578
|
+
|
|
579
|
+
# ==================== ROLES ====================
|
|
580
|
+
|
|
581
|
+
async def get_roles(
|
|
582
|
+
self, application_id: str, guild_id: str
|
|
583
|
+
) -> List[Dict[str, Any]]:
|
|
584
|
+
"""Get roles"""
|
|
585
|
+
result = await self.request("GET", f"/sdk/roles/{application_id}/{guild_id}")
|
|
586
|
+
return result.get("roles", [])
|
|
587
|
+
|
|
588
|
+
async def create_role(
|
|
589
|
+
self,
|
|
590
|
+
application_id: str,
|
|
591
|
+
guild_id: str,
|
|
592
|
+
name: Optional[str] = None,
|
|
593
|
+
permissions: Optional[str] = None,
|
|
594
|
+
color: Optional[int] = None,
|
|
595
|
+
hoist: Optional[bool] = None,
|
|
596
|
+
mentionable: Optional[bool] = None,
|
|
597
|
+
) -> Dict[str, Any]:
|
|
598
|
+
"""Create role"""
|
|
599
|
+
data: Dict[str, Any] = {
|
|
600
|
+
"applicationId": application_id,
|
|
601
|
+
"guildId": guild_id,
|
|
602
|
+
}
|
|
603
|
+
if name:
|
|
604
|
+
data["name"] = name
|
|
605
|
+
if permissions:
|
|
606
|
+
data["permissions"] = permissions
|
|
607
|
+
if color is not None:
|
|
608
|
+
data["color"] = color
|
|
609
|
+
if hoist is not None:
|
|
610
|
+
data["hoist"] = hoist
|
|
611
|
+
if mentionable is not None:
|
|
612
|
+
data["mentionable"] = mentionable
|
|
613
|
+
result = await self.request("POST", "/sdk/roles/create", data)
|
|
614
|
+
return result.get("role", {})
|
|
615
|
+
|
|
616
|
+
async def modify_role(
|
|
617
|
+
self,
|
|
618
|
+
application_id: str,
|
|
619
|
+
guild_id: str,
|
|
620
|
+
role_id: str,
|
|
621
|
+
name: Optional[str] = None,
|
|
622
|
+
permissions: Optional[str] = None,
|
|
623
|
+
color: Optional[int] = None,
|
|
624
|
+
hoist: Optional[bool] = None,
|
|
625
|
+
mentionable: Optional[bool] = None,
|
|
626
|
+
) -> Dict[str, Any]:
|
|
627
|
+
"""Modify role"""
|
|
628
|
+
data: Dict[str, Any] = {
|
|
629
|
+
"applicationId": application_id,
|
|
630
|
+
"guildId": guild_id,
|
|
631
|
+
"roleId": role_id,
|
|
632
|
+
}
|
|
633
|
+
if name:
|
|
634
|
+
data["name"] = name
|
|
635
|
+
if permissions:
|
|
636
|
+
data["permissions"] = permissions
|
|
637
|
+
if color is not None:
|
|
638
|
+
data["color"] = color
|
|
639
|
+
if hoist is not None:
|
|
640
|
+
data["hoist"] = hoist
|
|
641
|
+
if mentionable is not None:
|
|
642
|
+
data["mentionable"] = mentionable
|
|
643
|
+
result = await self.request("POST", "/sdk/roles/modify", data)
|
|
644
|
+
return result.get("role", {})
|
|
645
|
+
|
|
646
|
+
async def delete_role(
|
|
647
|
+
self, application_id: str, guild_id: str, role_id: str
|
|
648
|
+
) -> None:
|
|
649
|
+
"""Delete role"""
|
|
650
|
+
await self.request("POST", "/sdk/roles/delete", {
|
|
651
|
+
"applicationId": application_id,
|
|
652
|
+
"guildId": guild_id,
|
|
653
|
+
"roleId": role_id,
|
|
654
|
+
})
|
|
655
|
+
|
|
656
|
+
async def reorder_roles(
|
|
657
|
+
self,
|
|
658
|
+
application_id: str,
|
|
659
|
+
guild_id: str,
|
|
660
|
+
positions: List[Dict[str, Any]],
|
|
661
|
+
) -> List[Dict[str, Any]]:
|
|
662
|
+
"""Reorder roles"""
|
|
663
|
+
result = await self.request("POST", "/sdk/roles/reorder", {
|
|
664
|
+
"applicationId": application_id,
|
|
665
|
+
"guildId": guild_id,
|
|
666
|
+
"positions": positions,
|
|
667
|
+
})
|
|
668
|
+
return result.get("roles", [])
|
|
669
|
+
|
|
670
|
+
# ==================== WEBHOOKS ====================
|
|
671
|
+
|
|
672
|
+
async def create_webhook(
|
|
673
|
+
self,
|
|
674
|
+
application_id: str,
|
|
675
|
+
channel_id: str,
|
|
676
|
+
name: str,
|
|
677
|
+
avatar: Optional[str] = None,
|
|
678
|
+
) -> Dict[str, Any]:
|
|
679
|
+
"""Create webhook"""
|
|
680
|
+
data: Dict[str, Any] = {
|
|
681
|
+
"applicationId": application_id,
|
|
682
|
+
"channelId": channel_id,
|
|
683
|
+
"name": name,
|
|
684
|
+
}
|
|
685
|
+
if avatar:
|
|
686
|
+
data["avatar"] = avatar
|
|
687
|
+
result = await self.request("POST", "/sdk/webhooks/create", data)
|
|
688
|
+
return result.get("webhook", {})
|
|
689
|
+
|
|
690
|
+
async def get_channel_webhooks(
|
|
691
|
+
self, application_id: str, channel_id: str
|
|
692
|
+
) -> List[Dict[str, Any]]:
|
|
693
|
+
"""Get channel webhooks"""
|
|
694
|
+
result = await self.request(
|
|
695
|
+
"GET", f"/sdk/webhooks/channel/{application_id}/{channel_id}"
|
|
696
|
+
)
|
|
697
|
+
return result.get("webhooks", [])
|
|
698
|
+
|
|
699
|
+
async def get_guild_webhooks(
|
|
700
|
+
self, application_id: str, guild_id: str
|
|
701
|
+
) -> List[Dict[str, Any]]:
|
|
702
|
+
"""Get guild webhooks"""
|
|
703
|
+
result = await self.request(
|
|
704
|
+
"GET", f"/sdk/webhooks/guild/{application_id}/{guild_id}"
|
|
705
|
+
)
|
|
706
|
+
return result.get("webhooks", [])
|
|
707
|
+
|
|
708
|
+
async def get_webhook(
|
|
709
|
+
self, application_id: str, webhook_id: str
|
|
710
|
+
) -> Dict[str, Any]:
|
|
711
|
+
"""Get webhook"""
|
|
712
|
+
result = await self.request(
|
|
713
|
+
"GET", f"/sdk/webhooks/{application_id}/{webhook_id}"
|
|
714
|
+
)
|
|
715
|
+
return result.get("webhook", {})
|
|
716
|
+
|
|
717
|
+
async def modify_webhook(
|
|
718
|
+
self,
|
|
719
|
+
application_id: str,
|
|
720
|
+
webhook_id: str,
|
|
721
|
+
name: Optional[str] = None,
|
|
722
|
+
avatar: Optional[str] = None,
|
|
723
|
+
channel_id: Optional[str] = None,
|
|
724
|
+
) -> Dict[str, Any]:
|
|
725
|
+
"""Modify webhook"""
|
|
726
|
+
data: Dict[str, Any] = {
|
|
727
|
+
"applicationId": application_id,
|
|
728
|
+
"webhookId": webhook_id,
|
|
729
|
+
}
|
|
730
|
+
if name:
|
|
731
|
+
data["name"] = name
|
|
732
|
+
if avatar:
|
|
733
|
+
data["avatar"] = avatar
|
|
734
|
+
if channel_id:
|
|
735
|
+
data["channel_id"] = channel_id
|
|
736
|
+
result = await self.request("POST", "/sdk/webhooks/modify", data)
|
|
737
|
+
return result.get("webhook", {})
|
|
738
|
+
|
|
739
|
+
async def delete_webhook(
|
|
740
|
+
self, application_id: str, webhook_id: str
|
|
741
|
+
) -> None:
|
|
742
|
+
"""Delete webhook"""
|
|
743
|
+
await self.request("POST", "/sdk/webhooks/delete", {
|
|
744
|
+
"applicationId": application_id,
|
|
745
|
+
"webhookId": webhook_id,
|
|
746
|
+
})
|
|
747
|
+
|
|
748
|
+
async def execute_webhook(
|
|
749
|
+
self,
|
|
750
|
+
webhook_id: str,
|
|
751
|
+
webhook_token: str,
|
|
752
|
+
content: Optional[str] = None,
|
|
753
|
+
username: Optional[str] = None,
|
|
754
|
+
avatar_url: Optional[str] = None,
|
|
755
|
+
embeds: Optional[List[Dict[str, Any]]] = None,
|
|
756
|
+
wait: bool = False,
|
|
757
|
+
) -> Optional[Dict[str, Any]]:
|
|
758
|
+
"""Execute webhook (send message)"""
|
|
759
|
+
data: Dict[str, Any] = {
|
|
760
|
+
"webhookId": webhook_id,
|
|
761
|
+
"webhookToken": webhook_token,
|
|
762
|
+
"wait": wait,
|
|
763
|
+
}
|
|
764
|
+
if content:
|
|
765
|
+
data["content"] = content
|
|
766
|
+
if username:
|
|
767
|
+
data["username"] = username
|
|
768
|
+
if avatar_url:
|
|
769
|
+
data["avatar_url"] = avatar_url
|
|
770
|
+
if embeds:
|
|
771
|
+
data["embeds"] = embeds
|
|
772
|
+
result = await self.request("POST", "/sdk/webhooks/execute", data)
|
|
773
|
+
return result.get("message")
|
|
774
|
+
|
|
775
|
+
async def edit_webhook_message(
|
|
776
|
+
self,
|
|
777
|
+
webhook_id: str,
|
|
778
|
+
webhook_token: str,
|
|
779
|
+
message_id: str,
|
|
780
|
+
content: Optional[str] = None,
|
|
781
|
+
embeds: Optional[List[Dict[str, Any]]] = None,
|
|
782
|
+
) -> Dict[str, Any]:
|
|
783
|
+
"""Edit webhook message"""
|
|
784
|
+
data: Dict[str, Any] = {
|
|
785
|
+
"webhookId": webhook_id,
|
|
786
|
+
"webhookToken": webhook_token,
|
|
787
|
+
"messageId": message_id,
|
|
788
|
+
}
|
|
789
|
+
if content:
|
|
790
|
+
data["content"] = content
|
|
791
|
+
if embeds:
|
|
792
|
+
data["embeds"] = embeds
|
|
793
|
+
result = await self.request("POST", "/sdk/webhooks/message/edit", data)
|
|
794
|
+
return result.get("message", {})
|
|
795
|
+
|
|
796
|
+
async def delete_webhook_message(
|
|
797
|
+
self, webhook_id: str, webhook_token: str, message_id: str
|
|
798
|
+
) -> None:
|
|
799
|
+
"""Delete webhook message"""
|
|
800
|
+
await self.request("POST", "/sdk/webhooks/message/delete", {
|
|
801
|
+
"webhookId": webhook_id,
|
|
802
|
+
"webhookToken": webhook_token,
|
|
803
|
+
"messageId": message_id,
|
|
804
|
+
})
|
|
805
|
+
|