dshellInterpreter 0.1.10__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.10 → 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.10 → dshellinterpreter-0.1.11}/Dshell/DISCORD_COMMANDS/dshell_message.py +7 -7
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/Dshell/_DshellInterpreteur/dshell_interpreter.py +88 -63
- dshellinterpreter-0.1.11/Dshell/_DshellParser/ast_nodes.py +317 -0
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/Dshell/_DshellParser/dshell_parser.py +3 -1
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/Dshell/_DshellTokenizer/dshell_keywords.py +10 -8
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/Dshell/_DshellTokenizer/dshell_token_type.py +1 -1
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/Dshell/_DshellTokenizer/dshell_tokenizer.py +3 -2
- {dshellinterpreter-0.1.10/dshellInterpreter.egg-info → dshellinterpreter-0.1.11}/PKG-INFO +1 -1
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11/dshellInterpreter.egg-info}/PKG-INFO +1 -1
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/dshellInterpreter.egg-info/SOURCES.txt +1 -0
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/setup.py +1 -1
- dshellinterpreter-0.1.10/Dshell/DISCORD_COMMANDS/dshell_channel.py +0 -64
- dshellinterpreter-0.1.10/Dshell/_DshellParser/ast_nodes.py +0 -214
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/Dshell/_DshellInterpreteur/__init__.py +0 -0
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/Dshell/_DshellParser/__init__.py +0 -0
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/Dshell/_DshellTokenizer/__init__.py +0 -0
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/Dshell/__init__.py +0 -0
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/LICENSE +0 -0
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/README.md +0 -0
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/dshellInterpreter.egg-info/dependency_links.txt +0 -0
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/dshellInterpreter.egg-info/requires.txt +0 -0
- {dshellinterpreter-0.1.10 → dshellinterpreter-0.1.11}/dshellInterpreter.egg-info/top_level.txt +0 -0
- {dshellinterpreter-0.1.10 → 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.10 → 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)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from asyncio import sleep
|
|
2
2
|
from re import findall
|
|
3
3
|
from typing import TypeVar, Union, Any, Optional, Callable
|
|
4
|
-
from logging import Logger
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
from discord import AutoShardedBot, Embed, Colour, PermissionOverwrite, Permissions, Guild, Member, Role
|
|
@@ -20,12 +19,17 @@ context = TypeVar('context', AutoShardedBot, GuildChannel, PrivateChannel)
|
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
class DshellInterpreteur:
|
|
22
|
+
"""
|
|
23
|
+
Discord Dshell interpreter.
|
|
24
|
+
Make what you want with Dshell code to interact with Discord !
|
|
25
|
+
"""
|
|
23
26
|
|
|
24
|
-
def __init__(self,
|
|
27
|
+
def __init__(self, code: str, ctx: context, debug: bool = False):
|
|
25
28
|
"""
|
|
26
|
-
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.
|
|
27
31
|
"""
|
|
28
|
-
self.ast: list[ASTNode] = parse(DshellTokenizer(
|
|
32
|
+
self.ast: list[ASTNode] = parse(DshellTokenizer(code).start(), StartNode([]))[0]
|
|
29
33
|
self.env: dict[str, Any] = {}
|
|
30
34
|
self.ctx: context = ctx
|
|
31
35
|
if debug:
|
|
@@ -33,7 +37,16 @@ class DshellInterpreteur:
|
|
|
33
37
|
|
|
34
38
|
async def execute(self, ast: Optional[list[All_nodes]] = None):
|
|
35
39
|
"""
|
|
36
|
-
|
|
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.
|
|
37
50
|
"""
|
|
38
51
|
if ast is None:
|
|
39
52
|
ast = self.ast
|
|
@@ -92,19 +105,20 @@ class DshellInterpreteur:
|
|
|
92
105
|
elif isinstance(node, SleepNode):
|
|
93
106
|
sleep_time = eval_expression(node.body, self)
|
|
94
107
|
if sleep_time > 3600:
|
|
95
|
-
raise Exception(f
|
|
108
|
+
raise Exception(f"Sleep time is too long! ({sleep_time} seconds) - maximum is 3600 seconds)")
|
|
96
109
|
elif sleep_time < 1:
|
|
97
|
-
raise Exception(f
|
|
110
|
+
raise Exception(f"Sleep time is too short! ({sleep_time} seconds) - minimum is 1 second)")
|
|
98
111
|
|
|
99
112
|
await sleep(sleep_time)
|
|
100
113
|
|
|
101
114
|
|
|
102
115
|
elif isinstance(node, EndNode):
|
|
103
|
-
raise RuntimeError(
|
|
116
|
+
raise RuntimeError("Execution stopped - EndNode encountered")
|
|
104
117
|
|
|
105
118
|
def eval_data_token(self, token: Token):
|
|
106
119
|
"""
|
|
107
|
-
|
|
120
|
+
Eval a data token and returns its value in Python.
|
|
121
|
+
:param token: The token to evaluate.
|
|
108
122
|
"""
|
|
109
123
|
|
|
110
124
|
if not hasattr(token, 'type'):
|
|
@@ -137,7 +151,9 @@ class DshellInterpreteur:
|
|
|
137
151
|
|
|
138
152
|
def eval_expression_inline(if_node: IfNode, interpreter: DshellInterpreteur) -> Token:
|
|
139
153
|
"""
|
|
140
|
-
|
|
154
|
+
Eval a conditional expression inline.
|
|
155
|
+
:param if_node: The IfNode to evaluate.
|
|
156
|
+
:param interpreter: The Dshell interpreter instance.
|
|
141
157
|
"""
|
|
142
158
|
if eval_expression(if_node.condition, interpreter):
|
|
143
159
|
return eval_expression(if_node.body, interpreter)
|
|
@@ -147,7 +163,9 @@ def eval_expression_inline(if_node: IfNode, interpreter: DshellInterpreteur) ->
|
|
|
147
163
|
|
|
148
164
|
def eval_expression(tokens: list[Token], interpreter: DshellInterpreteur) -> Any:
|
|
149
165
|
"""
|
|
150
|
-
|
|
166
|
+
Evaluates an arithmetic and logical expression.
|
|
167
|
+
:param tokens: A list of tokens representing the expression.
|
|
168
|
+
:param interpreter: The Dshell interpreter instance.
|
|
151
169
|
"""
|
|
152
170
|
postfix = to_postfix(tokens)
|
|
153
171
|
stack = []
|
|
@@ -172,24 +190,28 @@ def eval_expression(tokens: list[Token], interpreter: DshellInterpreteur) -> Any
|
|
|
172
190
|
stack.append(result)
|
|
173
191
|
|
|
174
192
|
else:
|
|
175
|
-
raise SyntaxError(f"
|
|
193
|
+
raise SyntaxError(f"Unexpected token type: {token.type} - {token.value}")
|
|
176
194
|
|
|
177
195
|
if len(stack) != 1:
|
|
178
|
-
raise SyntaxError("
|
|
196
|
+
raise SyntaxError("Invalid expression: stack should contain exactly one element after evaluation.")
|
|
179
197
|
|
|
180
198
|
return stack[0]
|
|
181
199
|
|
|
182
200
|
|
|
183
201
|
async def call_function(function: Callable, args: ArgsCommandNode, interpreter: DshellInterpreteur):
|
|
184
202
|
"""
|
|
185
|
-
|
|
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.
|
|
186
208
|
"""
|
|
187
209
|
reformatted = regroupe_commandes(args.body, interpreter)[0]
|
|
188
210
|
|
|
189
211
|
# conversion des args en valeurs Python
|
|
190
212
|
absolute_args = reformatted.pop('*', list())
|
|
191
213
|
|
|
192
|
-
reformatted: dict[str, Token]
|
|
214
|
+
reformatted: dict[str, Token]
|
|
193
215
|
|
|
194
216
|
absolute_args.insert(0, interpreter.ctx)
|
|
195
217
|
keyword_args = {
|
|
@@ -200,64 +222,69 @@ async def call_function(function: Callable, args: ArgsCommandNode, interpreter:
|
|
|
200
222
|
|
|
201
223
|
def regroupe_commandes(body: list[Token], interpreter: DshellInterpreteur) -> list[dict[str, list[Any]]]:
|
|
202
224
|
"""
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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.
|
|
207
234
|
"""
|
|
208
|
-
tokens = {'*': []} #
|
|
209
|
-
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
|
|
210
237
|
n = len(body)
|
|
211
238
|
list_tokens: list[dict] = [tokens]
|
|
212
239
|
|
|
213
240
|
i = 0
|
|
214
241
|
while i < n:
|
|
215
242
|
if body[i].type == DTT.SEPARATOR and body[
|
|
216
|
-
i + 1].type == DTT.IDENT: #
|
|
217
|
-
current_arg = body[i + 1].value #
|
|
218
|
-
tokens[current_arg] = '' #
|
|
219
|
-
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
|
|
220
247
|
|
|
221
248
|
elif body[
|
|
222
|
-
i].type == DTT.SUB_SEPARATOR: #
|
|
249
|
+
i].type == DTT.SUB_SEPARATOR: # allows to delimit parameters and to have several with the same name
|
|
223
250
|
list_tokens += regroupe_commandes(
|
|
224
251
|
[Token(
|
|
225
252
|
type_=DTT.SEPARATOR, value=body[i].value, position=body[i].position)
|
|
226
253
|
] + body[i + 1:], interpreter
|
|
227
|
-
) #
|
|
254
|
+
) # add a sub-dictionary for sub-commands
|
|
228
255
|
return list_tokens
|
|
229
256
|
|
|
230
257
|
else:
|
|
231
258
|
if current_arg == '*':
|
|
232
259
|
tokens[current_arg].append(interpreter.eval_data_token(body[i]))
|
|
233
260
|
else:
|
|
234
|
-
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
|
|
235
262
|
i += 1
|
|
236
263
|
return list_tokens
|
|
237
264
|
|
|
238
265
|
|
|
239
266
|
def build_embed(body: list[Token], fields: list[FieldEmbedNode], interpreter: DshellInterpreteur) -> Embed:
|
|
240
267
|
"""
|
|
241
|
-
|
|
268
|
+
Builds an embed from the command information.
|
|
242
269
|
"""
|
|
243
270
|
args_main_embed: dict[str, list[Any]] = regroupe_commandes(body, interpreter)[0]
|
|
244
|
-
args_main_embed.pop('*') #
|
|
245
|
-
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
|
|
246
273
|
|
|
247
274
|
args_fields: list[dict[str, Token]] = []
|
|
248
|
-
for field in fields: #
|
|
275
|
+
for field in fields: # do the same for the fields
|
|
249
276
|
a = regroupe_commandes(field.body, interpreter)[0]
|
|
250
277
|
a.pop('*')
|
|
251
278
|
a: dict[str, Token]
|
|
252
279
|
args_fields.append(a)
|
|
253
280
|
|
|
254
281
|
if 'color' in args_main_embed and isinstance(args_main_embed['color'],
|
|
255
|
-
ListNode): #
|
|
282
|
+
ListNode): # if color is a ListNode, convert it to Colour
|
|
256
283
|
args_main_embed['color'] = Colour.from_rgb(*args_main_embed['color'])
|
|
257
284
|
|
|
258
|
-
embed = Embed(**args_main_embed) #
|
|
285
|
+
embed = Embed(**args_main_embed) # build the main embed
|
|
259
286
|
for field in args_fields:
|
|
260
|
-
embed.add_field(**field) #
|
|
287
|
+
embed.add_field(**field) # add all fields
|
|
261
288
|
|
|
262
289
|
return embed
|
|
263
290
|
|
|
@@ -265,7 +292,7 @@ def build_embed(body: list[Token], fields: list[FieldEmbedNode], interpreter: Ds
|
|
|
265
292
|
def build_permission(body: list[Token], interpreter: DshellInterpreteur) -> dict[
|
|
266
293
|
Union[Member, Role], PermissionOverwrite]:
|
|
267
294
|
"""
|
|
268
|
-
|
|
295
|
+
Builds a dictionary of PermissionOverwrite objects from the command information.
|
|
269
296
|
"""
|
|
270
297
|
args_permissions: list[dict[str, list[Any]]] = regroupe_commandes(body, interpreter)
|
|
271
298
|
permissions: dict[Union[Member, Role], PermissionOverwrite] = {}
|
|
@@ -279,7 +306,7 @@ def build_permission(body: list[Token], interpreter: DshellInterpreteur) -> dict
|
|
|
279
306
|
|
|
280
307
|
class DshellIterator:
|
|
281
308
|
"""
|
|
282
|
-
|
|
309
|
+
Used to transform anything into an iterable
|
|
283
310
|
"""
|
|
284
311
|
|
|
285
312
|
def __init__(self, data):
|
|
@@ -303,20 +330,20 @@ class DshellPermissions:
|
|
|
303
330
|
|
|
304
331
|
def __init__(self, target: dict[str, list[int]]):
|
|
305
332
|
"""
|
|
306
|
-
|
|
307
|
-
:param target:
|
|
308
|
-
|
|
309
|
-
|
|
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.
|
|
310
337
|
"""
|
|
311
338
|
self.target: dict[str, Union[ListNode, int]] = target
|
|
312
339
|
|
|
313
340
|
@staticmethod
|
|
314
341
|
def get_instance(guild: Guild, target_id: int) -> Union[Member, Role]:
|
|
315
342
|
"""
|
|
316
|
-
|
|
317
|
-
:param guild:
|
|
318
|
-
:param target_id:
|
|
319
|
-
: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
|
|
320
347
|
"""
|
|
321
348
|
try:
|
|
322
349
|
member = DshellPermissions.get_member(guild, target_id)
|
|
@@ -334,48 +361,46 @@ class DshellPermissions:
|
|
|
334
361
|
if role is not None:
|
|
335
362
|
return role
|
|
336
363
|
|
|
337
|
-
raise ValueError(f"Aucun membre ou rôle trouvé avec l'ID {target_id} dans le serveur {guild.name}.")
|
|
338
|
-
|
|
339
364
|
@staticmethod
|
|
340
365
|
def get_member(guild: Guild, target_id: int) -> Member:
|
|
341
366
|
"""
|
|
342
|
-
|
|
343
|
-
:param guild:
|
|
344
|
-
:param target_id:
|
|
345
|
-
: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
|
|
346
371
|
"""
|
|
347
372
|
member = guild.get_member(target_id)
|
|
348
373
|
if member is not None:
|
|
349
374
|
return member
|
|
350
375
|
|
|
351
|
-
raise ValueError(f"
|
|
376
|
+
raise ValueError(f"No member found with ID {target_id} in guild {guild.name}.")
|
|
352
377
|
|
|
353
378
|
@staticmethod
|
|
354
379
|
def get_role(guild: Guild, target_id: int) -> Role:
|
|
355
380
|
"""
|
|
356
|
-
|
|
357
|
-
:param guild:
|
|
358
|
-
:param target_id:
|
|
359
|
-
: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
|
|
360
385
|
"""
|
|
361
386
|
role = guild.get_role(target_id)
|
|
362
387
|
if role is not None:
|
|
363
388
|
return role
|
|
364
389
|
|
|
365
|
-
raise ValueError(f"
|
|
390
|
+
raise ValueError(f"No role found with ID {target_id} in guild {guild.name}.")
|
|
366
391
|
|
|
367
392
|
def get_permission_overwrite(self, guild: Guild) -> dict[Union[Member, Role], PermissionOverwrite]:
|
|
368
393
|
"""
|
|
369
|
-
|
|
370
|
-
:param guild:
|
|
371
|
-
: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
|
|
372
397
|
"""
|
|
373
398
|
permissions: dict[Union[Member, Role], PermissionOverwrite] = {}
|
|
374
399
|
target_keys = self.target.keys()
|
|
375
400
|
|
|
376
401
|
if 'members' in target_keys:
|
|
377
402
|
for member_id in (
|
|
378
|
-
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
|
|
379
404
|
member = self.get_member(guild, member_id)
|
|
380
405
|
permissions[member] = PermissionOverwrite.from_pair(
|
|
381
406
|
allow=Permissions(permissions=self.target.get('allow', 0)),
|
|
@@ -384,13 +409,13 @@ class DshellPermissions:
|
|
|
384
409
|
|
|
385
410
|
elif 'roles' in target_keys:
|
|
386
411
|
for role_id in (
|
|
387
|
-
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
|
|
388
413
|
role = self.get_role(guild, role_id)
|
|
389
414
|
permissions[role] = PermissionOverwrite.from_pair(
|
|
390
415
|
allow=Permissions(permissions=self.target.get('allow', 0)),
|
|
391
416
|
deny=Permissions(permissions=self.target.get('deny', 0))
|
|
392
417
|
)
|
|
393
418
|
else:
|
|
394
|
-
raise ValueError("
|
|
419
|
+
raise ValueError("No members or roles specified in the permissions target.")
|
|
395
420
|
|
|
396
421
|
return permissions
|