dshellInterpreter 0.1.9__tar.gz → 0.1.11__tar.gz
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.
Potentially problematic release.
This version of dshellInterpreter might be problematic. Click here for more details.
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/DISCORD_COMMANDS/__init__.py +1 -0
- dshellinterpreter-0.1.11/Dshell/DISCORD_COMMANDS/dshell_channel.py +93 -0
- dshellinterpreter-0.1.11/Dshell/DISCORD_COMMANDS/dshell_member.py +54 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/DISCORD_COMMANDS/dshell_message.py +7 -7
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/_DshellInterpreteur/dshell_interpreter.py +90 -63
- dshellinterpreter-0.1.11/Dshell/_DshellParser/ast_nodes.py +317 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/_DshellParser/dshell_parser.py +3 -1
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/_DshellTokenizer/dshell_keywords.py +10 -8
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/_DshellTokenizer/dshell_token_type.py +1 -1
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/_DshellTokenizer/dshell_tokenizer.py +3 -2
- {dshellinterpreter-0.1.9/dshellInterpreter.egg-info → dshellinterpreter-0.1.11}/PKG-INFO +1 -1
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11/dshellInterpreter.egg-info}/PKG-INFO +1 -1
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/dshellInterpreter.egg-info/SOURCES.txt +1 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/setup.py +1 -1
- dshellinterpreter-0.1.9/Dshell/DISCORD_COMMANDS/dshell_channel.py +0 -64
- dshellinterpreter-0.1.9/Dshell/_DshellParser/ast_nodes.py +0 -214
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/_DshellInterpreteur/__init__.py +0 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/_DshellParser/__init__.py +0 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/_DshellTokenizer/__init__.py +0 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/__init__.py +0 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/LICENSE +0 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/README.md +0 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/dshellInterpreter.egg-info/dependency_links.txt +0 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/dshellInterpreter.egg-info/requires.txt +0 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/dshellInterpreter.egg-info/top_level.txt +0 -0
- {dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/setup.cfg +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from asyncio import sleep
|
|
2
|
+
from re import search
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
from discord import MISSING, PermissionOverwrite, Member, Role
|
|
6
|
+
from discord.abc import GuildChannel
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
'dshell_create_text_channel',
|
|
10
|
+
'dshell_delete_channel',
|
|
11
|
+
'dshell_delete_channels',
|
|
12
|
+
'dshell_create_voice_channel'
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def dshell_create_text_channel(ctx: GuildChannel,
|
|
17
|
+
name,
|
|
18
|
+
category=None,
|
|
19
|
+
position=MISSING,
|
|
20
|
+
slowmode=MISSING,
|
|
21
|
+
topic=MISSING, nsfw=MISSING,
|
|
22
|
+
permission: dict[Union[Member, Role], PermissionOverwrite] = MISSING,
|
|
23
|
+
reason=None):
|
|
24
|
+
"""
|
|
25
|
+
Creates a text channel on the server
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
channel_category = ctx.guild.get_channel(category)
|
|
29
|
+
|
|
30
|
+
created_channel = await ctx.guild.create_text_channel(name,
|
|
31
|
+
category=channel_category,
|
|
32
|
+
position=position,
|
|
33
|
+
slowmode_delay=slowmode,
|
|
34
|
+
topic=topic,
|
|
35
|
+
nsfw=nsfw,
|
|
36
|
+
overwrites=permission,
|
|
37
|
+
reason=reason)
|
|
38
|
+
|
|
39
|
+
return created_channel.id
|
|
40
|
+
|
|
41
|
+
async def dshell_create_voice_channel(ctx: GuildChannel,
|
|
42
|
+
name,
|
|
43
|
+
category=None,
|
|
44
|
+
position=MISSING,
|
|
45
|
+
bitrate=MISSING,
|
|
46
|
+
permission: dict[Union[Member, Role], PermissionOverwrite] = MISSING,
|
|
47
|
+
reason=None):
|
|
48
|
+
"""
|
|
49
|
+
Creates a voice channel on the server
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
channel_category = ctx.guild.get_channel(category)
|
|
53
|
+
|
|
54
|
+
created_channel = await ctx.guild.create_voice_channel(name,
|
|
55
|
+
category=channel_category,
|
|
56
|
+
position=position,
|
|
57
|
+
bitrate=bitrate,
|
|
58
|
+
overwrites=permission,
|
|
59
|
+
reason=reason)
|
|
60
|
+
|
|
61
|
+
return created_channel.id
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
async def dshell_delete_channel(ctx: GuildChannel, channel=None, reason=None, timeout=0):
|
|
65
|
+
"""
|
|
66
|
+
Deletes a channel.
|
|
67
|
+
You can add a waiting time before it is deleted (in seconds)
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
channel_to_delete = ctx if channel is None else ctx.guild.get_channel(channel)
|
|
71
|
+
|
|
72
|
+
if channel_to_delete is None:
|
|
73
|
+
raise Exception(f"Le channel {channel} n'existe pas !")
|
|
74
|
+
|
|
75
|
+
await sleep(timeout)
|
|
76
|
+
|
|
77
|
+
await channel_to_delete.delete(reason=reason)
|
|
78
|
+
|
|
79
|
+
return channel_to_delete.id
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def dshell_delete_channels(ctx: GuildChannel, name=None, regex=None, reason=None):
|
|
83
|
+
"""
|
|
84
|
+
Deletes all channels with the same name and/or matching the same regex.
|
|
85
|
+
If neither is set, it will delete all channels with the same name as the one where the command was executed.
|
|
86
|
+
"""
|
|
87
|
+
for channel in ctx.guild.channels:
|
|
88
|
+
|
|
89
|
+
if name is not None and channel.name == str(name):
|
|
90
|
+
await channel.delete(reason=reason)
|
|
91
|
+
|
|
92
|
+
elif regex is not None and search(regex, channel.name):
|
|
93
|
+
await channel.delete(reason=reason)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from discord import Member, User, MISSING
|
|
2
|
+
from discord.abc import GuildChannel
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"dshell_ban_member",
|
|
7
|
+
"dshell_unban_member",
|
|
8
|
+
"dshell_kick_member"
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
async def dshell_ban_member(ctx: GuildChannel, member: int, reason: str = MISSING):
|
|
12
|
+
"""
|
|
13
|
+
Bans a member from the server.
|
|
14
|
+
"""
|
|
15
|
+
banned_member = ctx.guild.get_member(member)
|
|
16
|
+
|
|
17
|
+
if not banned_member:
|
|
18
|
+
return 1 # Member not found in the server
|
|
19
|
+
|
|
20
|
+
await ctx.guild.ban(banned_member, reason=reason)
|
|
21
|
+
|
|
22
|
+
return banned_member.id
|
|
23
|
+
|
|
24
|
+
async def dshell_unban_member(ctx: GuildChannel, user: int, reason: str = MISSING):
|
|
25
|
+
"""
|
|
26
|
+
Unbans a user from the server.
|
|
27
|
+
"""
|
|
28
|
+
banned_users = ctx.guild.bans()
|
|
29
|
+
user_to_unban = None
|
|
30
|
+
|
|
31
|
+
async for ban_entry in banned_users:
|
|
32
|
+
if ban_entry.user.id == user:
|
|
33
|
+
user_to_unban = ban_entry.user
|
|
34
|
+
break
|
|
35
|
+
|
|
36
|
+
if not user_to_unban:
|
|
37
|
+
return 1 # User not found in the banned list
|
|
38
|
+
|
|
39
|
+
await ctx.guild.unban(user_to_unban, reason=reason)
|
|
40
|
+
|
|
41
|
+
return user_to_unban.id
|
|
42
|
+
|
|
43
|
+
async def dshell_kick_member(ctx: GuildChannel, member: int, reason: str = MISSING):
|
|
44
|
+
"""
|
|
45
|
+
Kicks a member from the server.
|
|
46
|
+
"""
|
|
47
|
+
kicked_member = ctx.guild.get_member(member)
|
|
48
|
+
|
|
49
|
+
if not kicked_member:
|
|
50
|
+
return 1 # Member not found in the server
|
|
51
|
+
|
|
52
|
+
await ctx.guild.kick(kicked_member, reason=reason)
|
|
53
|
+
|
|
54
|
+
return kicked_member.id
|
{dshellinterpreter-0.1.9 → dshellinterpreter-0.1.11}/Dshell/DISCORD_COMMANDS/dshell_message.py
RENAMED
|
@@ -11,12 +11,12 @@ __all__ = [
|
|
|
11
11
|
async def dshell_send_message(ctx: GuildChannel, message=None, delete=None, channel=None, embeds=None, embed=None):
|
|
12
12
|
from .._DshellParser.ast_nodes import ListNode
|
|
13
13
|
"""
|
|
14
|
-
|
|
14
|
+
Sends a message on Discord
|
|
15
15
|
"""
|
|
16
16
|
channel_to_send = ctx if channel is None else ctx.guild.get_channel(channel)
|
|
17
17
|
|
|
18
18
|
if channel_to_send is None:
|
|
19
|
-
raise Exception(f'
|
|
19
|
+
raise Exception(f'Channel {channel} not found!')
|
|
20
20
|
|
|
21
21
|
if embeds is None:
|
|
22
22
|
embeds = ListNode([])
|
|
@@ -36,25 +36,25 @@ async def dshell_send_message(ctx: GuildChannel, message=None, delete=None, chan
|
|
|
36
36
|
|
|
37
37
|
async def dshell_delete_message(ctx: GuildChannel, message, reason=None, delay=0):
|
|
38
38
|
"""
|
|
39
|
-
|
|
39
|
+
Deletes a message
|
|
40
40
|
"""
|
|
41
41
|
|
|
42
|
-
delete_message = ctx.get_partial_message(message) #
|
|
42
|
+
delete_message = ctx.get_partial_message(message) # builds a reference to the message (even if it doesn't exist)
|
|
43
43
|
|
|
44
44
|
if delay > 3600:
|
|
45
|
-
raise Exception(f'
|
|
45
|
+
raise Exception(f'The message deletion delay is too long! ({delay} seconds)')
|
|
46
46
|
|
|
47
47
|
await delete_message.delete(delay=delay, reason=reason)
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
async def dshell_purge_message(ctx: GuildChannel, message_number, channel=None, reason=None):
|
|
51
51
|
"""
|
|
52
|
-
|
|
52
|
+
Purges messages from a channel
|
|
53
53
|
"""
|
|
54
54
|
|
|
55
55
|
purge_channel = ctx if channel is None else ctx.guild.get_channel(channel)
|
|
56
56
|
|
|
57
57
|
if purge_channel is None:
|
|
58
|
-
raise Exception(f"
|
|
58
|
+
raise Exception(f"Channel {channel} to purge not found!")
|
|
59
59
|
|
|
60
60
|
await purge_channel.purge(limit=message_number, reason=reason)
|
|
@@ -2,6 +2,7 @@ from asyncio import sleep
|
|
|
2
2
|
from re import findall
|
|
3
3
|
from typing import TypeVar, Union, Any, Optional, Callable
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
from discord import AutoShardedBot, Embed, Colour, PermissionOverwrite, Permissions, Guild, Member, Role
|
|
6
7
|
from discord.abc import GuildChannel, PrivateChannel
|
|
7
8
|
|
|
@@ -18,12 +19,17 @@ context = TypeVar('context', AutoShardedBot, GuildChannel, PrivateChannel)
|
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class DshellInterpreteur:
|
|
22
|
+
"""
|
|
23
|
+
Discord Dshell interpreter.
|
|
24
|
+
Make what you want with Dshell code to interact with Discord !
|
|
25
|
+
"""
|
|
21
26
|
|
|
22
|
-
def __init__(self,
|
|
27
|
+
def __init__(self, code: str, ctx: context, debug: bool = False):
|
|
23
28
|
"""
|
|
24
|
-
Interpreter Dshell code
|
|
29
|
+
Interpreter Dshell code
|
|
30
|
+
:param code: The code to interpret. Each line must end with a newline character, except SEPARATOR and SUB_SEPARATOR tokens.
|
|
25
31
|
"""
|
|
26
|
-
self.ast: list[ASTNode] = parse(DshellTokenizer(
|
|
32
|
+
self.ast: list[ASTNode] = parse(DshellTokenizer(code).start(), StartNode([]))[0]
|
|
27
33
|
self.env: dict[str, Any] = {}
|
|
28
34
|
self.ctx: context = ctx
|
|
29
35
|
if debug:
|
|
@@ -31,7 +37,16 @@ class DshellInterpreteur:
|
|
|
31
37
|
|
|
32
38
|
async def execute(self, ast: Optional[list[All_nodes]] = None):
|
|
33
39
|
"""
|
|
34
|
-
|
|
40
|
+
Executes the abstract syntax tree (AST) generated from the Dshell code.
|
|
41
|
+
|
|
42
|
+
This asynchronous method traverses and interprets each node in the AST, executing commands,
|
|
43
|
+
handling control flow structures (such as if, elif, else, and loops), managing variables,
|
|
44
|
+
and interacting with Discord through the provided context. It supports command execution,
|
|
45
|
+
variable assignment, sleep operations, and permission handling, among other features.
|
|
46
|
+
|
|
47
|
+
:param ast: Optional list of AST nodes to execute. If None, uses the interpreter's main AST.
|
|
48
|
+
:raises RuntimeError: If an EndNode is encountered, indicating execution should be stopped.
|
|
49
|
+
:raises Exception: If sleep duration is out of allowed bounds.
|
|
35
50
|
"""
|
|
36
51
|
if ast is None:
|
|
37
52
|
ast = self.ast
|
|
@@ -90,19 +105,20 @@ class DshellInterpreteur:
|
|
|
90
105
|
elif isinstance(node, SleepNode):
|
|
91
106
|
sleep_time = eval_expression(node.body, self)
|
|
92
107
|
if sleep_time > 3600:
|
|
93
|
-
raise Exception(f
|
|
108
|
+
raise Exception(f"Sleep time is too long! ({sleep_time} seconds) - maximum is 3600 seconds)")
|
|
94
109
|
elif sleep_time < 1:
|
|
95
|
-
raise Exception(f
|
|
110
|
+
raise Exception(f"Sleep time is too short! ({sleep_time} seconds) - minimum is 1 second)")
|
|
96
111
|
|
|
97
112
|
await sleep(sleep_time)
|
|
98
113
|
|
|
99
114
|
|
|
100
115
|
elif isinstance(node, EndNode):
|
|
101
|
-
raise RuntimeError(
|
|
116
|
+
raise RuntimeError("Execution stopped - EndNode encountered")
|
|
102
117
|
|
|
103
118
|
def eval_data_token(self, token: Token):
|
|
104
119
|
"""
|
|
105
|
-
|
|
120
|
+
Eval a data token and returns its value in Python.
|
|
121
|
+
:param token: The token to evaluate.
|
|
106
122
|
"""
|
|
107
123
|
|
|
108
124
|
if not hasattr(token, 'type'):
|
|
@@ -135,7 +151,9 @@ class DshellInterpreteur:
|
|
|
135
151
|
|
|
136
152
|
def eval_expression_inline(if_node: IfNode, interpreter: DshellInterpreteur) -> Token:
|
|
137
153
|
"""
|
|
138
|
-
|
|
154
|
+
Eval a conditional expression inline.
|
|
155
|
+
:param if_node: The IfNode to evaluate.
|
|
156
|
+
:param interpreter: The Dshell interpreter instance.
|
|
139
157
|
"""
|
|
140
158
|
if eval_expression(if_node.condition, interpreter):
|
|
141
159
|
return eval_expression(if_node.body, interpreter)
|
|
@@ -145,7 +163,9 @@ def eval_expression_inline(if_node: IfNode, interpreter: DshellInterpreteur) ->
|
|
|
145
163
|
|
|
146
164
|
def eval_expression(tokens: list[Token], interpreter: DshellInterpreteur) -> Any:
|
|
147
165
|
"""
|
|
148
|
-
|
|
166
|
+
Evaluates an arithmetic and logical expression.
|
|
167
|
+
:param tokens: A list of tokens representing the expression.
|
|
168
|
+
:param interpreter: The Dshell interpreter instance.
|
|
149
169
|
"""
|
|
150
170
|
postfix = to_postfix(tokens)
|
|
151
171
|
stack = []
|
|
@@ -170,24 +190,28 @@ def eval_expression(tokens: list[Token], interpreter: DshellInterpreteur) -> Any
|
|
|
170
190
|
stack.append(result)
|
|
171
191
|
|
|
172
192
|
else:
|
|
173
|
-
raise SyntaxError(f"
|
|
193
|
+
raise SyntaxError(f"Unexpected token type: {token.type} - {token.value}")
|
|
174
194
|
|
|
175
195
|
if len(stack) != 1:
|
|
176
|
-
raise SyntaxError("
|
|
196
|
+
raise SyntaxError("Invalid expression: stack should contain exactly one element after evaluation.")
|
|
177
197
|
|
|
178
198
|
return stack[0]
|
|
179
199
|
|
|
180
200
|
|
|
181
201
|
async def call_function(function: Callable, args: ArgsCommandNode, interpreter: DshellInterpreteur):
|
|
182
202
|
"""
|
|
183
|
-
|
|
203
|
+
Call the function with the given arguments.
|
|
204
|
+
It can be an async function !
|
|
205
|
+
:param function: The function to call.
|
|
206
|
+
:param args: The arguments to pass to the function.
|
|
207
|
+
:param interpreter: The Dshell interpreter instance.
|
|
184
208
|
"""
|
|
185
209
|
reformatted = regroupe_commandes(args.body, interpreter)[0]
|
|
186
210
|
|
|
187
211
|
# conversion des args en valeurs Python
|
|
188
212
|
absolute_args = reformatted.pop('*', list())
|
|
189
213
|
|
|
190
|
-
reformatted: dict[str, Token]
|
|
214
|
+
reformatted: dict[str, Token]
|
|
191
215
|
|
|
192
216
|
absolute_args.insert(0, interpreter.ctx)
|
|
193
217
|
keyword_args = {
|
|
@@ -198,64 +222,69 @@ async def call_function(function: Callable, args: ArgsCommandNode, interpreter:
|
|
|
198
222
|
|
|
199
223
|
def regroupe_commandes(body: list[Token], interpreter: DshellInterpreteur) -> list[dict[str, list[Any]]]:
|
|
200
224
|
"""
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
225
|
+
Groups the command arguments in the form of a python dictionary.
|
|
226
|
+
Note that you can specify the parameter you wish to pass via -- followed by the parameter name. But this is not mandatory!
|
|
227
|
+
Non-mandatory parameters will be stored in a list in the form of tokens with the key ‘*’.
|
|
228
|
+
The others, having been specified via a separator, will be in the form of a list of tokens with the IDENT token as key, following the separator for each argument.
|
|
229
|
+
If two parameters have the same name, the last one will overwrite the previous one.
|
|
230
|
+
To accept duplicates, use the SUB_SEPARATOR (~~) to create a sub-dictionary for parameters with the same name.
|
|
231
|
+
|
|
232
|
+
:param body: The list of tokens to group.
|
|
233
|
+
:param interpreter: The Dshell interpreter instance.
|
|
205
234
|
"""
|
|
206
|
-
tokens = {'*': []} #
|
|
207
|
-
current_arg = '*' #
|
|
235
|
+
tokens = {'*': []} # tokens to return
|
|
236
|
+
current_arg = '*' # the argument keys are the types they belong to. '*' is for all arguments not explicitly specified by a separator and an IDENT
|
|
208
237
|
n = len(body)
|
|
209
238
|
list_tokens: list[dict] = [tokens]
|
|
210
239
|
|
|
211
240
|
i = 0
|
|
212
241
|
while i < n:
|
|
213
242
|
if body[i].type == DTT.SEPARATOR and body[
|
|
214
|
-
i + 1].type == DTT.IDENT: #
|
|
215
|
-
current_arg = body[i + 1].value #
|
|
216
|
-
tokens[current_arg] = '' #
|
|
217
|
-
i += 2 #
|
|
243
|
+
i + 1].type == DTT.IDENT: # Check if it's a separator and if the next token is an IDENT
|
|
244
|
+
current_arg = body[i + 1].value # change the current argument. It will be impossible to return to '*'
|
|
245
|
+
tokens[current_arg] = '' # create a key/value pair for it
|
|
246
|
+
i += 2 # skip the IDENT after the separator since it has just been processed
|
|
218
247
|
|
|
219
248
|
elif body[
|
|
220
|
-
i].type == DTT.SUB_SEPARATOR: #
|
|
249
|
+
i].type == DTT.SUB_SEPARATOR: # allows to delimit parameters and to have several with the same name
|
|
221
250
|
list_tokens += regroupe_commandes(
|
|
222
251
|
[Token(
|
|
223
252
|
type_=DTT.SEPARATOR, value=body[i].value, position=body[i].position)
|
|
224
253
|
] + body[i + 1:], interpreter
|
|
225
|
-
) #
|
|
254
|
+
) # add a sub-dictionary for sub-commands
|
|
226
255
|
return list_tokens
|
|
227
256
|
|
|
228
257
|
else:
|
|
229
258
|
if current_arg == '*':
|
|
230
259
|
tokens[current_arg].append(interpreter.eval_data_token(body[i]))
|
|
231
260
|
else:
|
|
232
|
-
tokens[current_arg] = interpreter.eval_data_token(body[i]) #
|
|
261
|
+
tokens[current_arg] = interpreter.eval_data_token(body[i]) # add the token to the current argument
|
|
233
262
|
i += 1
|
|
234
263
|
return list_tokens
|
|
235
264
|
|
|
236
265
|
|
|
237
266
|
def build_embed(body: list[Token], fields: list[FieldEmbedNode], interpreter: DshellInterpreteur) -> Embed:
|
|
238
267
|
"""
|
|
239
|
-
|
|
268
|
+
Builds an embed from the command information.
|
|
240
269
|
"""
|
|
241
270
|
args_main_embed: dict[str, list[Any]] = regroupe_commandes(body, interpreter)[0]
|
|
242
|
-
args_main_embed.pop('*') #
|
|
243
|
-
args_main_embed: dict[str, Token] #
|
|
271
|
+
args_main_embed.pop('*') # remove unspecified parameters for the embed
|
|
272
|
+
args_main_embed: dict[str, Token] # specify what it contains from now on
|
|
244
273
|
|
|
245
274
|
args_fields: list[dict[str, Token]] = []
|
|
246
|
-
for field in fields: #
|
|
275
|
+
for field in fields: # do the same for the fields
|
|
247
276
|
a = regroupe_commandes(field.body, interpreter)[0]
|
|
248
277
|
a.pop('*')
|
|
249
278
|
a: dict[str, Token]
|
|
250
279
|
args_fields.append(a)
|
|
251
280
|
|
|
252
281
|
if 'color' in args_main_embed and isinstance(args_main_embed['color'],
|
|
253
|
-
ListNode): #
|
|
282
|
+
ListNode): # if color is a ListNode, convert it to Colour
|
|
254
283
|
args_main_embed['color'] = Colour.from_rgb(*args_main_embed['color'])
|
|
255
284
|
|
|
256
|
-
embed = Embed(**args_main_embed) #
|
|
285
|
+
embed = Embed(**args_main_embed) # build the main embed
|
|
257
286
|
for field in args_fields:
|
|
258
|
-
embed.add_field(**field) #
|
|
287
|
+
embed.add_field(**field) # add all fields
|
|
259
288
|
|
|
260
289
|
return embed
|
|
261
290
|
|
|
@@ -263,7 +292,7 @@ def build_embed(body: list[Token], fields: list[FieldEmbedNode], interpreter: Ds
|
|
|
263
292
|
def build_permission(body: list[Token], interpreter: DshellInterpreteur) -> dict[
|
|
264
293
|
Union[Member, Role], PermissionOverwrite]:
|
|
265
294
|
"""
|
|
266
|
-
|
|
295
|
+
Builds a dictionary of PermissionOverwrite objects from the command information.
|
|
267
296
|
"""
|
|
268
297
|
args_permissions: list[dict[str, list[Any]]] = regroupe_commandes(body, interpreter)
|
|
269
298
|
permissions: dict[Union[Member, Role], PermissionOverwrite] = {}
|
|
@@ -272,12 +301,12 @@ def build_permission(body: list[Token], interpreter: DshellInterpreteur) -> dict
|
|
|
272
301
|
i.pop('*')
|
|
273
302
|
permissions.update(DshellPermissions(i).get_permission_overwrite(interpreter.ctx.guild))
|
|
274
303
|
|
|
275
|
-
|
|
304
|
+
return permissions
|
|
276
305
|
|
|
277
306
|
|
|
278
307
|
class DshellIterator:
|
|
279
308
|
"""
|
|
280
|
-
|
|
309
|
+
Used to transform anything into an iterable
|
|
281
310
|
"""
|
|
282
311
|
|
|
283
312
|
def __init__(self, data):
|
|
@@ -301,20 +330,20 @@ class DshellPermissions:
|
|
|
301
330
|
|
|
302
331
|
def __init__(self, target: dict[str, list[int]]):
|
|
303
332
|
"""
|
|
304
|
-
|
|
305
|
-
:param target:
|
|
306
|
-
|
|
307
|
-
|
|
333
|
+
Creates a Dshell permissions object.
|
|
334
|
+
:param target: A dictionary containing parameters and their values.
|
|
335
|
+
Expected parameters: “allow”, “deny”, ‘members’, “roles”.
|
|
336
|
+
For “members” and “roles”, values must be ID ListNodes.
|
|
308
337
|
"""
|
|
309
338
|
self.target: dict[str, Union[ListNode, int]] = target
|
|
310
339
|
|
|
311
340
|
@staticmethod
|
|
312
341
|
def get_instance(guild: Guild, target_id: int) -> Union[Member, Role]:
|
|
313
342
|
"""
|
|
314
|
-
|
|
315
|
-
:param guild:
|
|
316
|
-
:param target_id:
|
|
317
|
-
:return:
|
|
343
|
+
Returns the instance corresponding to the given id. Only a Member or Role.
|
|
344
|
+
:param guild: The Discord server in which to search
|
|
345
|
+
:param target_id: The ID of the member or role
|
|
346
|
+
:return: An instance of Member or Role
|
|
318
347
|
"""
|
|
319
348
|
try:
|
|
320
349
|
member = DshellPermissions.get_member(guild, target_id)
|
|
@@ -332,48 +361,46 @@ class DshellPermissions:
|
|
|
332
361
|
if role is not None:
|
|
333
362
|
return role
|
|
334
363
|
|
|
335
|
-
raise ValueError(f"Aucun membre ou rôle trouvé avec l'ID {target_id} dans le serveur {guild.name}.")
|
|
336
|
-
|
|
337
364
|
@staticmethod
|
|
338
365
|
def get_member(guild: Guild, target_id: int) -> Member:
|
|
339
366
|
"""
|
|
340
|
-
|
|
341
|
-
:param guild:
|
|
342
|
-
:param target_id:
|
|
343
|
-
:return:
|
|
367
|
+
Returns the Member instance corresponding to the given id.
|
|
368
|
+
:param guild: The Discord server to search
|
|
369
|
+
:param target_id: The member ID
|
|
370
|
+
:return: A Member instance
|
|
344
371
|
"""
|
|
345
372
|
member = guild.get_member(target_id)
|
|
346
373
|
if member is not None:
|
|
347
374
|
return member
|
|
348
375
|
|
|
349
|
-
raise ValueError(f"
|
|
376
|
+
raise ValueError(f"No member found with ID {target_id} in guild {guild.name}.")
|
|
350
377
|
|
|
351
378
|
@staticmethod
|
|
352
379
|
def get_role(guild: Guild, target_id: int) -> Role:
|
|
353
380
|
"""
|
|
354
|
-
|
|
355
|
-
:param guild:
|
|
356
|
-
:param target_id:
|
|
357
|
-
:return:
|
|
381
|
+
Returns the Role instance corresponding to the given id.
|
|
382
|
+
:param guild: The Discord server to search
|
|
383
|
+
:param target_id: The role ID
|
|
384
|
+
:return: A Role instance
|
|
358
385
|
"""
|
|
359
386
|
role = guild.get_role(target_id)
|
|
360
387
|
if role is not None:
|
|
361
388
|
return role
|
|
362
389
|
|
|
363
|
-
raise ValueError(f"
|
|
390
|
+
raise ValueError(f"No role found with ID {target_id} in guild {guild.name}.")
|
|
364
391
|
|
|
365
392
|
def get_permission_overwrite(self, guild: Guild) -> dict[Union[Member, Role], PermissionOverwrite]:
|
|
366
393
|
"""
|
|
367
|
-
|
|
368
|
-
:param guild:
|
|
369
|
-
:return:
|
|
394
|
+
Returns a PermissionOverwrite object with member and role permissions.
|
|
395
|
+
:param guild: The Discord server
|
|
396
|
+
:return: A dictionary of PermissionOverwrite objects with members and roles as keys
|
|
370
397
|
"""
|
|
371
398
|
permissions: dict[Union[Member, Role], PermissionOverwrite] = {}
|
|
372
399
|
target_keys = self.target.keys()
|
|
373
400
|
|
|
374
401
|
if 'members' in target_keys:
|
|
375
402
|
for member_id in (
|
|
376
|
-
self.target['members'] if isinstance(self.target['members'], ListNode) else [self.target['members']]): #
|
|
403
|
+
self.target['members'] if isinstance(self.target['members'], ListNode) else [self.target['members']]): # allow a single ID
|
|
377
404
|
member = self.get_member(guild, member_id)
|
|
378
405
|
permissions[member] = PermissionOverwrite.from_pair(
|
|
379
406
|
allow=Permissions(permissions=self.target.get('allow', 0)),
|
|
@@ -382,13 +409,13 @@ class DshellPermissions:
|
|
|
382
409
|
|
|
383
410
|
elif 'roles' in target_keys:
|
|
384
411
|
for role_id in (
|
|
385
|
-
self.target['roles'] if isinstance(self.target['roles'], ListNode) else [self.target['roles']]):
|
|
412
|
+
self.target['roles'] if isinstance(self.target['roles'], ListNode) else [self.target['roles']]): # allow a single ID
|
|
386
413
|
role = self.get_role(guild, role_id)
|
|
387
414
|
permissions[role] = PermissionOverwrite.from_pair(
|
|
388
415
|
allow=Permissions(permissions=self.target.get('allow', 0)),
|
|
389
416
|
deny=Permissions(permissions=self.target.get('deny', 0))
|
|
390
417
|
)
|
|
391
418
|
else:
|
|
392
|
-
raise ValueError("
|
|
419
|
+
raise ValueError("No members or roles specified in the permissions target.")
|
|
393
420
|
|
|
394
421
|
return permissions
|