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/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
+