elos 1.0.1__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.
ELOS/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ import sys as _sys
2
+ from .runner import run, run_source
3
+
4
+ __all__ = ["run", "run_source"]
5
+ __version__ = "2.0.0"
6
+
7
+ _sys.modules.pop("ELOS.runner", None)
8
+ _sys.modules.pop("ELOS._bot", None)
9
+ _sys.modules.pop("ELOS._gateway", None)
10
+ _sys.modules.pop("ELOS._http", None)
11
+ _sys.modules.pop("ELOS._runtime", None)
12
+ _sys.modules.pop("ELOS._parser", None)
13
+ _sys.modules.pop("ELOS._lexer", None)
14
+ _sys.modules.pop("ELOS._ast", None)
15
+ del _sys
ELOS/__main__.py ADDED
@@ -0,0 +1,24 @@
1
+ import sys
2
+ import argparse
3
+ from pathlib import Path
4
+
5
+ def main():
6
+ parser = argparse.ArgumentParser(prog="python -m ELOS")
7
+ parser.add_argument("file", help=".elos source file to run")
8
+ parser.add_argument("--user-id", type=int, default=0, metavar="INT")
9
+ parser.add_argument("--env", action="append", default=[], metavar="KEY=VALUE")
10
+ args = parser.parse_args()
11
+
12
+ extra_env = {}
13
+ for item in args.env:
14
+ if "=" not in item:
15
+ print(f"[ELOS] Invalid --env: {item}", file=sys.stderr)
16
+ sys.exit(1)
17
+ k, v = item.split("=", 1)
18
+ extra_env[k] = v
19
+
20
+ from ELOS.runner import run
21
+ sys.exit(run(args.file, user_id=args.user_id, env=extra_env or None))
22
+
23
+ if __name__ == "__main__":
24
+ main()
ELOS/_ast.py ADDED
@@ -0,0 +1,86 @@
1
+ import re
2
+
3
+ class Node:
4
+ line: int = None
5
+
6
+ class Program(Node):
7
+ def __init__(self, events, funcdefs=None, login_token=None):
8
+ self.events = events
9
+ self.funcdefs = funcdefs or []
10
+ self.login_token = login_token
11
+
12
+ class EventBlock(Node):
13
+ def __init__(self, event_type, params, body):
14
+ self.event_type = event_type; self.params = params; self.body = body
15
+
16
+ class FuncDef(Node):
17
+ def __init__(self, name, params, body):
18
+ self.name = name; self.params = params; self.body = body
19
+
20
+ class AssignStmt(Node):
21
+ def __init__(self, target, value): self.target = target; self.value = value
22
+
23
+ class IfStmt(Node):
24
+ def __init__(self, cond, body, elifs=None, else_body=None):
25
+ self.cond = cond; self.body = body
26
+ self.elifs = elifs or []; self.else_body = else_body or []
27
+
28
+ class ForStmt(Node):
29
+ def __init__(self, var, iterable, body):
30
+ self.var = var; self.iterable = iterable; self.body = body
31
+
32
+ class WhileStmt(Node):
33
+ def __init__(self, cond, body): self.cond = cond; self.body = body
34
+
35
+ class TryCatch(Node):
36
+ def __init__(self, try_body, catch_body, catch_var=None):
37
+ self.try_body = try_body; self.catch_body = catch_body; self.catch_var = catch_var
38
+
39
+ class ReturnStmt(Node):
40
+ def __init__(self, value=None): self.value = value
41
+
42
+ class BreakStmt(Node): pass
43
+ class ContinueStmt(Node): pass
44
+ class StopStmt(Node): pass
45
+
46
+ class ExprStmt(Node):
47
+ def __init__(self, expr): self.expr = expr
48
+
49
+ class CallExpr(Node):
50
+ def __init__(self, func, args): self.func = func; self.args = args
51
+
52
+ class IndexExpr(Node):
53
+ def __init__(self, obj, idx): self.obj = obj; self.idx = idx
54
+
55
+ class BinOp(Node):
56
+ def __init__(self, left, op, right): self.left = left; self.op = op; self.right = right
57
+
58
+ class UnaryOp(Node):
59
+ def __init__(self, op, operand): self.op = op; self.operand = operand
60
+
61
+ class Ident(Node):
62
+ def __init__(self, name): self.name = name
63
+
64
+ class StringLit(Node):
65
+ def __init__(self, value): self.value = value
66
+
67
+ class NumberLit(Node):
68
+ def __init__(self, value): self.value = value
69
+
70
+ class BoolLit(Node):
71
+ def __init__(self, value): self.value = value
72
+
73
+ class NullLit(Node): pass
74
+
75
+ class ListLit(Node):
76
+ def __init__(self, items): self.items = items
77
+
78
+ class FStringLit(Node):
79
+ def __init__(self, parts): self.parts = parts
80
+
81
+ class EmbedLit(Node):
82
+ def __init__(self): self.fields = []
83
+
84
+ class TernaryExpr(Node):
85
+ def __init__(self, true_val, condition, false_val):
86
+ self.true_val = true_val; self.condition = condition; self.false_val = false_val
ELOS/_bot.py ADDED
@@ -0,0 +1,89 @@
1
+ import asyncio
2
+ import os
3
+ import sys
4
+ from typing import Optional
5
+
6
+ from ._lexer import Lexer
7
+ from ._parser import Parser
8
+ from ._ast import Program, EventBlock, FuncDef
9
+ from ._http import HTTP
10
+ from ._gateway import Gateway
11
+ from ._runtime import Interpreter, StopSignal, _red, _yellow
12
+
13
+ class Bot:
14
+ def __init__(self, token: str, user_id: int = 0):
15
+ self.token = token
16
+ self.user_id = user_id
17
+ self._http = HTTP(token)
18
+ self._gw: Optional[Gateway] = None
19
+ self._interp: Optional[Interpreter] = None
20
+ self._handlers: list[EventBlock] = []
21
+ self._timer_handlers: list[EventBlock] = []
22
+ self._program: Optional[Program] = None
23
+
24
+ def load_source(self, source: str):
25
+ tokens = Lexer(source).tokenize()
26
+ program = Parser(tokens).parse()
27
+ self._program = program
28
+
29
+ self._interp = Interpreter(self._http)
30
+ if self.user_id:
31
+ var_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '.elos_vars')
32
+ os.makedirs(var_dir, exist_ok=True)
33
+ self._interp._var_file = os.path.join(var_dir, f'user_{self.user_id}.json')
34
+
35
+ for fd in program.funcdefs:
36
+ self._interp.user_funcs[fd.name] = fd
37
+
38
+ self._handlers = program.events
39
+ self._timer_handlers = [e for e in program.events if e.event_type in ('onTimerEnd', 'onTimerStart')]
40
+
41
+ async def _timer_dispatch(timer_id: str):
42
+ await self._dispatch_elos('onTimerEnd', {'timer_id': timer_id})
43
+ self._interp._timer_dispatch = _timer_dispatch
44
+
45
+ return program
46
+
47
+ async def _dispatch_elos(self, elos_event: str, ctx: dict):
48
+ for handler in self._handlers:
49
+ if handler.event_type != elos_event:
50
+ continue
51
+ env = dict(self._interp.globals)
52
+ for i, param in enumerate(handler.params):
53
+ env[param] = list(ctx.values())[i] if i < len(ctx) else None
54
+ try:
55
+ from ._runtime import ReturnSignal, BreakSignal
56
+ await self._interp._exec_block(handler.body, env)
57
+ except StopSignal:
58
+ pass
59
+ except Exception as e:
60
+ line = getattr(handler, 'line', '?')
61
+ _red(f"[ELOS] {type(e).__name__}: {e} [Event: {elos_event}, Line: {line}]")
62
+
63
+ async def _on_gateway_event(self, event: str, data: dict):
64
+ if not self._interp:
65
+ return
66
+
67
+ if event == "READY":
68
+ self._interp._bot_user = data.get("user", {})
69
+ self._interp.application_id = self._gw.application_id
70
+ await self._interp.run_event(self._handlers, event, data)
71
+ return
72
+
73
+ await self._interp.run_event(self._handlers, event, data)
74
+
75
+ async def start(self):
76
+ self._gw = Gateway(self.token, self._on_gateway_event)
77
+ try:
78
+ await self._gw.connect()
79
+ except asyncio.CancelledError:
80
+ pass
81
+ finally:
82
+ await self._http.close()
83
+ if self._gw:
84
+ await self._gw.close()
85
+
86
+ async def close(self):
87
+ if self._gw:
88
+ await self._gw.close()
89
+ await self._http.close()
ELOS/_gateway.py ADDED
@@ -0,0 +1,159 @@
1
+ import asyncio
2
+ import json
3
+ import sys
4
+ import time
5
+ import aiohttp
6
+ from typing import Callable, Optional
7
+
8
+ RED = "\033[91m"
9
+ YELLOW= "\033[93m"
10
+ RESET = "\033[0m"
11
+
12
+ GW_URL = "wss://gateway.discord.gg/?v=10&encoding=json"
13
+
14
+ OP_DISPATCH = 0
15
+ OP_HEARTBEAT = 1
16
+ OP_IDENTIFY = 2
17
+ OP_RESUME = 6
18
+ OP_RECONNECT = 7
19
+ OP_INVALID_SESSION = 9
20
+ OP_HELLO = 10
21
+ OP_HEARTBEAT_ACK = 11
22
+
23
+ INTENT_GUILDS = 1 << 0
24
+ INTENT_GUILD_MEMBERS = 1 << 1
25
+ INTENT_GUILD_MODERATION = 1 << 2
26
+ INTENT_GUILD_EMOJIS = 1 << 3
27
+ INTENT_GUILD_MESSAGES = 1 << 9
28
+ INTENT_GUILD_REACTIONS = 1 << 10
29
+ INTENT_GUILD_TYPING = 1 << 11
30
+ INTENT_DM_MESSAGES = 1 << 12
31
+ INTENT_MESSAGE_CONTENT = 1 << 15
32
+ INTENT_GUILD_VOICE = 1 << 7
33
+
34
+ ALL_INTENTS = (
35
+ INTENT_GUILDS | INTENT_GUILD_MEMBERS | INTENT_GUILD_MODERATION |
36
+ INTENT_GUILD_EMOJIS | INTENT_GUILD_MESSAGES | INTENT_GUILD_REACTIONS |
37
+ INTENT_GUILD_TYPING | INTENT_DM_MESSAGES | INTENT_MESSAGE_CONTENT |
38
+ INTENT_GUILD_VOICE
39
+ )
40
+
41
+ class Gateway:
42
+ def __init__(self, token: str, dispatch: Callable):
43
+ self.token = token
44
+ self.dispatch = dispatch
45
+ self._ws: Optional[aiohttp.ClientWebSocketResponse] = None
46
+ self._session: Optional[aiohttp.ClientSession] = None
47
+ self._seq: Optional[int] = None
48
+ self._session_id: Optional[str] = None
49
+ self._heartbeat_interval: float = 41.25
50
+ self._last_ack: float = time.monotonic()
51
+ self._running = False
52
+ self.application_id: Optional[str] = None
53
+ self.user: Optional[dict] = None
54
+
55
+ async def connect(self):
56
+ self._running = True
57
+ self._session = aiohttp.ClientSession()
58
+ while self._running:
59
+ try:
60
+ await self._run()
61
+ except asyncio.CancelledError:
62
+ break
63
+ except Exception as e:
64
+ print(f"{RED}[ELOS] Gateway error: {e}{RESET}", file=sys.stderr)
65
+ if self._running:
66
+ print(f"{YELLOW}[ELOS] Reconnecting in 5s...{RESET}", file=sys.stderr)
67
+ await asyncio.sleep(5)
68
+
69
+ async def _run(self):
70
+ async with self._session.ws_connect(GW_URL) as ws:
71
+ self._ws = ws
72
+ hello = await ws.receive_json()
73
+ self._heartbeat_interval = hello["d"]["heartbeat_interval"] / 1000.0
74
+
75
+ hb_task = asyncio.create_task(self._heartbeat_loop())
76
+ try:
77
+ if self._session_id:
78
+ await self._resume(ws)
79
+ else:
80
+ await self._identify(ws)
81
+
82
+ async for msg in ws:
83
+ if msg.type == aiohttp.WSMsgType.TEXT:
84
+ await self._handle(json.loads(msg.data))
85
+ elif msg.type in (aiohttp.WSMsgType.CLOSED, aiohttp.WSMsgType.ERROR):
86
+ break
87
+ finally:
88
+ hb_task.cancel()
89
+ try: await hb_task
90
+ except asyncio.CancelledError: pass
91
+
92
+ async def _identify(self, ws):
93
+ await ws.send_json({
94
+ "op": OP_IDENTIFY,
95
+ "d": {
96
+ "token": self.token,
97
+ "intents": ALL_INTENTS,
98
+ "properties": {"os": "linux", "browser": "ELOS", "device": "ELOS"},
99
+ }
100
+ })
101
+
102
+ async def _resume(self, ws):
103
+ await ws.send_json({
104
+ "op": OP_RESUME,
105
+ "d": {
106
+ "token": self.token,
107
+ "session_id": self._session_id,
108
+ "seq": self._seq,
109
+ }
110
+ })
111
+
112
+ async def _heartbeat_loop(self):
113
+ await asyncio.sleep(self._heartbeat_interval * 0.5)
114
+ while True:
115
+ if self._ws and not self._ws.closed:
116
+ await self._ws.send_json({"op": OP_HEARTBEAT, "d": self._seq})
117
+ await asyncio.sleep(self._heartbeat_interval)
118
+
119
+ async def _handle(self, msg: dict):
120
+ op = msg.get("op")
121
+ data = msg.get("d")
122
+ seq = msg.get("s")
123
+ event = msg.get("t")
124
+
125
+ if seq is not None:
126
+ self._seq = seq
127
+
128
+ if op == OP_DISPATCH:
129
+ if event == "READY":
130
+ self._session_id = data["session_id"]
131
+ self.user = data["user"]
132
+ self.application_id = data.get("application", {}).get("id") or data["user"]["id"]
133
+ print(f"[ELOS] Bot online: {data['user']['username']}#{data['user']['discriminator']}")
134
+ await self.dispatch(event, data)
135
+
136
+ elif op == OP_HEARTBEAT:
137
+ if self._ws:
138
+ await self._ws.send_json({"op": OP_HEARTBEAT, "d": self._seq})
139
+
140
+ elif op == OP_HEARTBEAT_ACK:
141
+ self._last_ack = time.monotonic()
142
+
143
+ elif op == OP_RECONNECT:
144
+ if self._ws: await self._ws.close()
145
+
146
+ elif op == OP_INVALID_SESSION:
147
+ resumable = bool(data)
148
+ if not resumable:
149
+ self._session_id = None
150
+ self._seq = None
151
+ await asyncio.sleep(2)
152
+ if self._ws: await self._ws.close()
153
+
154
+ async def close(self):
155
+ self._running = False
156
+ if self._ws and not self._ws.closed:
157
+ await self._ws.close()
158
+ if self._session and not self._session.closed:
159
+ await self._session.close()
ELOS/_http.py ADDED
@@ -0,0 +1,261 @@
1
+ import asyncio
2
+ import aiohttp
3
+ import json
4
+ import sys
5
+ from typing import Optional
6
+
7
+ YELLOW = "\033[93m"
8
+ RESET = "\033[0m"
9
+
10
+ API = "https://discord.com/api/v10"
11
+
12
+ class DiscordHTTPError(Exception):
13
+ def __init__(self, status: int, message: str, code: int = 0):
14
+ self.status = status
15
+ self.code = code
16
+ super().__init__(f"HTTP {status}: {message} (code={code})")
17
+
18
+ class HTTP:
19
+ def __init__(self, token: str):
20
+ self.token = token
21
+ self._session: Optional[aiohttp.ClientSession] = None
22
+ self._global_rl = asyncio.Event()
23
+ self._global_rl.set()
24
+
25
+ @property
26
+ def headers(self):
27
+ return {
28
+ "Authorization": f"Bot {self.token}",
29
+ "Content-Type": "application/json",
30
+ "User-Agent": "ELOS/1.0",
31
+ }
32
+
33
+ async def _session_get(self) -> aiohttp.ClientSession:
34
+ if self._session is None or self._session.closed:
35
+ self._session = aiohttp.ClientSession()
36
+ return self._session
37
+
38
+ async def request(self, method: str, path: str, **kwargs) -> dict | list | None:
39
+ await self._global_rl.wait()
40
+ session = await self._session_get()
41
+ url = f"{API}{path}"
42
+ headers = self.headers.copy()
43
+
44
+ data = kwargs.pop("json", None)
45
+ if data is not None:
46
+ kwargs["data"] = json.dumps(data)
47
+
48
+ form = kwargs.pop("form", None)
49
+
50
+ for attempt in range(5):
51
+ try:
52
+ if form:
53
+ resp = await session.request(method, url, headers={k: v for k, v in headers.items() if k != "Content-Type"}, data=form)
54
+ else:
55
+ resp = await session.request(method, url, headers=headers, **kwargs)
56
+
57
+ if resp.status == 429:
58
+ data_rl = await resp.json()
59
+ retry_after = data_rl.get("retry_after", 1.0)
60
+ is_global = data_rl.get("global", False)
61
+ print(f"{YELLOW}[ELOS] Rate limited. Retry after {retry_after}s (global={is_global}){RESET}", file=sys.stderr)
62
+ if is_global:
63
+ self._global_rl.clear()
64
+ await asyncio.sleep(retry_after)
65
+ self._global_rl.set()
66
+ else:
67
+ await asyncio.sleep(retry_after)
68
+ continue
69
+
70
+ if resp.status == 204:
71
+ return None
72
+
73
+ body = await resp.text()
74
+ try:
75
+ result = json.loads(body)
76
+ except Exception:
77
+ result = body
78
+
79
+ if resp.status >= 400:
80
+ msg = result.get("message", body) if isinstance(result, dict) else body
81
+ code = result.get("code", 0) if isinstance(result, dict) else 0
82
+ raise DiscordHTTPError(resp.status, msg, code)
83
+
84
+ remaining = resp.headers.get("X-RateLimit-Remaining")
85
+ reset_after = resp.headers.get("X-RateLimit-Reset-After")
86
+ if remaining == "0" and reset_after:
87
+ await asyncio.sleep(float(reset_after))
88
+
89
+ return result
90
+
91
+ except DiscordHTTPError:
92
+ raise
93
+ except aiohttp.ClientError as e:
94
+ if attempt == 4:
95
+ raise
96
+ await asyncio.sleep(1 + attempt)
97
+
98
+ return None
99
+
100
+ async def close(self):
101
+ if self._session and not self._session.closed:
102
+ await self._session.close()
103
+
104
+ async def send_message(self, channel_id, content=None, embed=None, components=None, reference=None, ephemeral=False, files=None):
105
+ payload = {}
106
+ if content is not None:
107
+ payload["content"] = str(content)
108
+ if embed is not None:
109
+ payload["embeds"] = [embed if isinstance(embed, dict) else embed.to_dict()]
110
+ if components:
111
+ payload["components"] = components
112
+ if reference:
113
+ payload["message_reference"] = {"message_id": str(reference)}
114
+ if files:
115
+ form = aiohttp.FormData()
116
+ form.add_field("payload_json", json.dumps(payload), content_type="application/json")
117
+ for i, (name, data, ct) in enumerate(files):
118
+ form.add_field(f"files[{i}]", data, filename=name, content_type=ct)
119
+ return await self.request("POST", f"/channels/{channel_id}/messages", form=form)
120
+ return await self.request("POST", f"/channels/{channel_id}/messages", json=payload)
121
+
122
+ async def delete_message(self, channel_id, message_id):
123
+ return await self.request("DELETE", f"/channels/{channel_id}/messages/{message_id}")
124
+
125
+ async def edit_message(self, channel_id, message_id, content=None, embed=None):
126
+ payload = {}
127
+ if content is not None: payload["content"] = str(content)
128
+ if embed is not None: payload["embeds"] = [embed if isinstance(embed, dict) else embed.to_dict()]
129
+ return await self.request("PATCH", f"/channels/{channel_id}/messages/{message_id}", json=payload)
130
+
131
+ async def pin_message(self, channel_id, message_id):
132
+ return await self.request("PUT", f"/channels/{channel_id}/pins/{message_id}")
133
+
134
+ async def unpin_message(self, channel_id, message_id):
135
+ return await self.request("DELETE", f"/channels/{channel_id}/pins/{message_id}")
136
+
137
+ async def add_reaction(self, channel_id, message_id, emoji):
138
+ emoji_enc = emoji.replace("#", "%23")
139
+ return await self.request("PUT", f"/channels/{channel_id}/messages/{message_id}/reactions/{emoji_enc}/@me")
140
+
141
+ async def remove_reaction(self, channel_id, message_id, emoji, user_id=None):
142
+ emoji_enc = emoji.replace("#", "%23")
143
+ target = f"/{user_id}" if user_id else "/@me"
144
+ return await self.request("DELETE", f"/channels/{channel_id}/messages/{message_id}/reactions/{emoji_enc}{target}")
145
+
146
+ async def get_message(self, channel_id, message_id):
147
+ return await self.request("GET", f"/channels/{channel_id}/messages/{message_id}")
148
+
149
+ async def get_channel(self, channel_id):
150
+ return await self.request("GET", f"/channels/{channel_id}")
151
+
152
+ async def create_channel(self, guild_id, name, type=0, topic=None, parent_id=None):
153
+ payload = {"name": name, "type": type}
154
+ if topic: payload["topic"] = topic
155
+ if parent_id: payload["parent_id"] = str(parent_id)
156
+ return await self.request("POST", f"/guilds/{guild_id}/channels", json=payload)
157
+
158
+ async def delete_channel(self, channel_id):
159
+ return await self.request("DELETE", f"/channels/{channel_id}")
160
+
161
+ async def edit_channel(self, channel_id, **kwargs):
162
+ return await self.request("PATCH", f"/channels/{channel_id}", json=kwargs)
163
+
164
+ async def get_guild_channels(self, guild_id):
165
+ return await self.request("GET", f"/guilds/{guild_id}/channels")
166
+
167
+ async def create_invite(self, channel_id, max_age=86400, max_uses=0, temporary=False):
168
+ return await self.request("POST", f"/channels/{channel_id}/invites",
169
+ json={"max_age": max_age, "max_uses": max_uses, "temporary": temporary})
170
+
171
+ async def get_guild(self, guild_id):
172
+ return await self.request("GET", f"/guilds/{guild_id}")
173
+
174
+ async def get_guild_members(self, guild_id, limit=1000):
175
+ return await self.request("GET", f"/guilds/{guild_id}/members?limit={limit}")
176
+
177
+ async def get_member(self, guild_id, user_id):
178
+ return await self.request("GET", f"/guilds/{guild_id}/members/{user_id}")
179
+
180
+ async def ban_member(self, guild_id, user_id, reason=None, delete_days=0):
181
+ payload = {"delete_message_days": delete_days}
182
+ headers = {}
183
+ if reason: headers["X-Audit-Log-Reason"] = reason
184
+ return await self.request("PUT", f"/guilds/{guild_id}/bans/{user_id}", json=payload)
185
+
186
+ async def unban_member(self, guild_id, user_id):
187
+ return await self.request("DELETE", f"/guilds/{guild_id}/bans/{user_id}")
188
+
189
+ async def kick_member(self, guild_id, user_id, reason=None):
190
+ return await self.request("DELETE", f"/guilds/{guild_id}/members/{user_id}")
191
+
192
+ async def add_role(self, guild_id, user_id, role_id):
193
+ return await self.request("PUT", f"/guilds/{guild_id}/members/{user_id}/roles/{role_id}")
194
+
195
+ async def remove_role(self, guild_id, user_id, role_id):
196
+ return await self.request("DELETE", f"/guilds/{guild_id}/members/{user_id}/roles/{role_id}")
197
+
198
+ async def edit_member(self, guild_id, user_id, **kwargs):
199
+ return await self.request("PATCH", f"/guilds/{guild_id}/members/{user_id}", json=kwargs)
200
+
201
+ async def get_guild_roles(self, guild_id):
202
+ return await self.request("GET", f"/guilds/{guild_id}/roles")
203
+
204
+ async def create_role(self, guild_id, name, color=0, permissions="0", mentionable=False):
205
+ return await self.request("POST", f"/guilds/{guild_id}/roles",
206
+ json={"name": name, "color": color, "permissions": permissions, "mentionable": mentionable})
207
+
208
+ async def delete_role(self, guild_id, role_id):
209
+ return await self.request("DELETE", f"/guilds/{guild_id}/roles/{role_id}")
210
+
211
+ async def edit_role(self, guild_id, role_id, **kwargs):
212
+ return await self.request("PATCH", f"/guilds/{guild_id}/roles/{role_id}", json=kwargs)
213
+
214
+ async def get_user(self, user_id):
215
+ return await self.request("GET", f"/users/{user_id}")
216
+
217
+ async def get_me(self):
218
+ return await self.request("GET", "/users/@me")
219
+
220
+ async def create_webhook(self, channel_id, name):
221
+ return await self.request("POST", f"/channels/{channel_id}/webhooks", json={"name": name})
222
+
223
+ async def delete_webhook(self, webhook_id):
224
+ return await self.request("DELETE", f"/webhooks/{webhook_id}")
225
+
226
+ async def execute_webhook(self, webhook_id, webhook_token, content=None, username=None, avatar_url=None, embeds=None):
227
+ payload = {}
228
+ if content: payload["content"] = str(content)
229
+ if username: payload["username"] = username
230
+ if avatar_url: payload["avatar_url"] = avatar_url
231
+ if embeds: payload["embeds"] = embeds
232
+ return await self.request("POST", f"/webhooks/{webhook_id}/{webhook_token}", json=payload)
233
+
234
+ async def interaction_response(self, interaction_id, interaction_token, type: int, data: dict = None):
235
+ payload = {"type": type}
236
+ if data: payload["data"] = data
237
+ return await self.request("POST", f"/interactions/{interaction_id}/{interaction_token}/callback", json=payload)
238
+
239
+ async def edit_interaction_response(self, application_id, interaction_token, content=None, embeds=None):
240
+ payload = {}
241
+ if content is not None: payload["content"] = str(content)
242
+ if embeds: payload["embeds"] = embeds
243
+ return await self.request("PATCH", f"/webhooks/{application_id}/{interaction_token}/messages/@original", json=payload)
244
+
245
+ async def register_global_command(self, application_id, command: dict):
246
+ return await self.request("POST", f"/applications/{application_id}/commands", json=command)
247
+
248
+ async def register_guild_command(self, application_id, guild_id, command: dict):
249
+ return await self.request("POST", f"/applications/{application_id}/guilds/{guild_id}/commands", json=command)
250
+
251
+ async def get_global_commands(self, application_id):
252
+ return await self.request("GET", f"/applications/{application_id}/commands")
253
+
254
+ async def delete_global_command(self, application_id, command_id):
255
+ return await self.request("DELETE", f"/applications/{application_id}/commands/{command_id}")
256
+
257
+ async def trigger_typing(self, channel_id):
258
+ return await self.request("POST", f"/channels/{channel_id}/typing")
259
+
260
+ async def create_dm(self, user_id):
261
+ return await self.request("POST", "/users/@me/channels", json={"recipient_id": str(user_id)})