dshellInterpreter 1.1.4.2__tar.gz → 1.1.4.4__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.
Files changed (49) hide show
  1. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/__init__.py +1 -0
  2. dshellinterpreter-1.1.4.4/Dshell/DISCORD_COMMANDS/dshell_file.py +68 -0
  3. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/dshell_message.py +41 -8
  4. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/utils/utils_type_validation.py +30 -0
  5. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellParser/ast_nodes.py +113 -2
  6. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellParser/dshell_parser.py +24 -15
  7. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellTokenizer/dshell_keywords.py +8 -2
  8. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellTokenizer/dshell_tokenizer.py +4 -3
  9. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/full_import.py +1 -3
  10. {dshellinterpreter-1.1.4.2/dshellInterpreter.egg-info → dshellinterpreter-1.1.4.4}/PKG-INFO +1 -1
  11. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4/dshellInterpreter.egg-info}/PKG-INFO +1 -1
  12. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/dshellInterpreter.egg-info/SOURCES.txt +1 -0
  13. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/setup.py +1 -1
  14. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/dshell_channel.py +0 -0
  15. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/dshell_embed.py +0 -0
  16. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/dshell_interaction.py +0 -0
  17. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/dshell_member.py +0 -0
  18. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/dshell_pastbin.py +0 -0
  19. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/dshell_role.py +0 -0
  20. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/dshell_ui.py +0 -0
  21. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/utils/__init__.py +0 -0
  22. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/utils/utils_global.py +0 -0
  23. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/utils/utils_list.py +0 -0
  24. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/utils/utils_member.py +0 -0
  25. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/utils/utils_message.py +0 -0
  26. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/utils/utils_numbers.py +0 -0
  27. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/utils/utils_permissions.py +0 -0
  28. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/utils/utils_string.py +0 -0
  29. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DISCORD_COMMANDS/utils/utils_thread.py +0 -0
  30. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellInterpreteur/__init__.py +0 -0
  31. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellInterpreteur/cached_messages.py +0 -0
  32. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellInterpreteur/dshell_arguments.py +0 -0
  33. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellInterpreteur/dshell_interpreter.py +0 -0
  34. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellInterpreteur/dshell_scope.py +0 -0
  35. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellInterpreteur/errors.py +0 -0
  36. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellInterpreteur/utils_interpreter.py +0 -0
  37. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellParser/__init__.py +0 -0
  38. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellPreProcess/__init__.py +0 -0
  39. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellPreProcess/dshell_preprocess.py +0 -0
  40. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellTokenizer/__init__.py +0 -0
  41. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/DshellTokenizer/dshell_token_type.py +0 -0
  42. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/__init__.py +0 -0
  43. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/Dshell/regex_test.py +0 -0
  44. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/LICENSE +0 -0
  45. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/README.md +0 -0
  46. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/dshellInterpreter.egg-info/dependency_links.txt +0 -0
  47. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/dshellInterpreter.egg-info/requires.txt +0 -0
  48. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/dshellInterpreter.egg-info/top_level.txt +0 -0
  49. {dshellinterpreter-1.1.4.2 → dshellinterpreter-1.1.4.4}/setup.cfg +0 -0
@@ -6,3 +6,4 @@ from .dshell_role import *
6
6
  from .dshell_interaction import *
7
7
  from .dshell_embed import *
8
8
  from .utils import *
9
+ from .dshell_file import *
@@ -0,0 +1,68 @@
1
+ from ..full_import import Message, Union, PartialMessage, Optional
2
+ from ..DshellParser.ast_nodes import ListNode, AttachmentNode, FileNode
3
+
4
+ from .utils.utils_message import utils_get_message
5
+ from .utils.utils_type_validation import (_validate_required_file_node,
6
+ _validate_required_string,
7
+ _validate_optional_string,
8
+ _validate_required_bool)
9
+
10
+ __all__ = [
11
+
12
+ ]
13
+
14
+ async def dshell_get_message_files(ctx: Message, message: Union[str, int]) -> ListNode:
15
+ """
16
+ Récupère les fichiers d'un message
17
+ :param ctx:
18
+ :param message:
19
+ :return:
20
+ """
21
+
22
+ target_message = ctx if message is None else utils_get_message(ctx, message)
23
+
24
+ attachments_list: ListNode = ListNode([], 0)
25
+
26
+ if isinstance(target_message, PartialMessage):
27
+ target_message = await target_message.fetch()
28
+
29
+ for attachment in target_message.attachments:
30
+ attachments_list.add(AttachmentNode(attachment))
31
+
32
+ return attachments_list
33
+
34
+ async def dshell_read_file(ctx: Message, file: AttachmentNode) -> str:
35
+ """
36
+ Lit en entier un fichier
37
+ :param ctx:
38
+ :param file:
39
+ :return:
40
+ """
41
+ _CMD = "rf"
42
+ _validate_required_file_node(file, 'file', _CMD)
43
+
44
+ return await file.read()
45
+
46
+ async def dshell_write_file(ctx: Message,
47
+ file: Optional[FileNode] = None,
48
+ message: str = "",
49
+ append: bool = False,
50
+ filename: Optional[str] = None) -> FileNode:
51
+ """
52
+ Ecrit dans un fichier
53
+ :param ctx:
54
+ :param file:
55
+ :param message:
56
+ :param append:
57
+ :param filename:
58
+ :return:
59
+ """
60
+ _validate_required_string(message, 'message', 'wf')
61
+ _validate_required_bool(append, 'append', 'wf')
62
+ _validate_optional_string(filename, 'filename', 'wf')
63
+
64
+ target_file = file if isinstance(file, FileNode) else FileNode(filename)
65
+
66
+ target_file.write(bytearray(message, "utf-8"), append)
67
+
68
+ return target_file
@@ -1,8 +1,9 @@
1
1
  from Dshell.full_import import (Message,
2
2
  Embed,
3
- PartialMessage)
3
+ PartialMessage,
4
+ File)
4
5
 
5
- from ..DshellParser.ast_nodes import ListNode, EvalNode
6
+ from ..DshellParser.ast_nodes import ListNode, FileNode
6
7
 
7
8
  from .utils.utils_message import utils_get_message, utils_autorised_mentions
8
9
  from .utils.utils_type_validation import (_validate_optional_number,
@@ -14,7 +15,9 @@ from .utils.utils_type_validation import (_validate_optional_number,
14
15
  _validate_required_bool,
15
16
  _validate_required_int,
16
17
  _validate_not_none,
17
- _validate_optional_eval_group_node)
18
+ _validate_optional_eval_group_node,
19
+ _validate_optional_list_node,
20
+ _validate_required_file_node)
18
21
  from ..DshellInterpreteur.cached_messages import dshell_cached_messages
19
22
 
20
23
  from Dshell.full_import import Optional, Union, compile, DOTALL
@@ -55,6 +58,7 @@ async def dshell_send_message(ctx: Message,
55
58
  users_mentions: bool = True,
56
59
  reply_mention: bool = False,
57
60
  embeds=None,
61
+ files: Optional[ListNode] = None,
58
62
  view=None) -> int:
59
63
  """
60
64
  Sends a message on Discord
@@ -67,6 +71,7 @@ async def dshell_send_message(ctx: Message,
67
71
  _validate_required_bool(roles_mentions, "Roles mentions", _CMD)
68
72
  _validate_required_bool(users_mentions, "Users mentions", _CMD)
69
73
  _validate_required_bool(reply_mention, "Reply mention", _CMD)
74
+ _validate_optional_list_node(files, "files", _CMD)
70
75
 
71
76
  channel_to_send = ctx.channel if channel is None else ctx.channel.guild.get_channel(channel)
72
77
  allowed_mentions = utils_autorised_mentions(global_mentions, everyone_mention, roles_mentions, users_mentions, reply_mention)
@@ -74,7 +79,13 @@ async def dshell_send_message(ctx: Message,
74
79
  if channel_to_send is None:
75
80
  raise Exception(f'Channel {channel} not found!')
76
81
 
82
+ if files is not None:
83
+ for file in files:
84
+ _validate_required_file_node(file, "file", _CMD)
77
85
 
86
+ final_files: list[File] = []
87
+ for file in files:
88
+ final_files.append(File(fp=file.content, filename=file.filename, description=file.description, spoiler=file.spoiler))
78
89
 
79
90
  _validate_optional_embed(embeds, "Embeds", _CMD)
80
91
 
@@ -90,7 +101,8 @@ async def dshell_send_message(ctx: Message,
90
101
  delete_after=delete,
91
102
  embeds=embeds,
92
103
  allowed_mentions=allowed_mentions,
93
- view=view)
104
+ view=view,
105
+ files=final_files)
94
106
 
95
107
  cached_messages = dshell_cached_messages.get()
96
108
  cached_messages[sended_message.id] = sended_message
@@ -108,6 +120,7 @@ async def dshell_respond_message(ctx: Message,
108
120
  users_mentions: bool = True,
109
121
  reply_mention: bool = False,
110
122
  delete=None,
123
+ files: Optional[ListNode] = None,
111
124
  embeds=None):
112
125
  """
113
126
  Responds to a message on Discord
@@ -121,11 +134,21 @@ async def dshell_respond_message(ctx: Message,
121
134
  _validate_required_bool(roles_mentions, "Roles mentions", _CMD)
122
135
  _validate_required_bool(users_mentions, "Users mentions", _CMD)
123
136
  _validate_required_bool(reply_mention, "Reply mention", _CMD)
137
+ _validate_optional_list_node(files, "files", _CMD)
124
138
 
125
139
  respond_message = ctx if message is None else utils_get_message(ctx, message) # builds a reference to the message (even if it doesn't exist)
126
140
  autorised_mentions = utils_autorised_mentions(global_mentions, everyone_mention, roles_mentions, users_mentions, reply_mention)
127
141
  mention_author = True if reply_mention else False
128
142
 
143
+ if files is not None:
144
+ for file in files:
145
+ _validate_required_file_node(file, "file", _CMD)
146
+
147
+ final_files: list[File] = []
148
+ for file in files:
149
+ final_files.append(
150
+ File(fp=file.content, filename=file.filename, description=file.description, spoiler=file.spoiler))
151
+
129
152
  _validate_optional_embed(embeds, "Embeds", _CMD)
130
153
 
131
154
  if embeds is None:
@@ -139,7 +162,8 @@ async def dshell_respond_message(ctx: Message,
139
162
  mention_author=mention_author,
140
163
  allowed_mentions=autorised_mentions,
141
164
  delete_after=delete,
142
- embeds=embeds)
165
+ embeds=embeds,
166
+ files=final_files)
143
167
 
144
168
  cached_messages = dshell_cached_messages.get()
145
169
  cached_messages[sended_message.id] = sended_message
@@ -179,7 +203,7 @@ async def dshell_purge_message(ctx: Message, message_number: int, channel: Optio
179
203
  await purge_channel.purge(limit=message_number, reason=reason)
180
204
 
181
205
 
182
- async def dshell_edit_message(ctx: Message, message, new_content=None, embeds=None, view=None) -> int:
206
+ async def dshell_edit_message(ctx: Message, message, new_content=None, embeds=None, view=None, files=None) -> int:
183
207
  """
184
208
  Edits a message
185
209
  """
@@ -188,8 +212,17 @@ async def dshell_edit_message(ctx: Message, message, new_content=None, embeds=No
188
212
  edit_message = utils_get_message(ctx, message)
189
213
 
190
214
  _validate_optional_embed(embeds, "Embeds", _CMD)
191
-
192
215
  _validate_optional_view(view, "View", _CMD)
216
+ _validate_optional_list_node(files, "files", _CMD)
217
+
218
+ if files is not None:
219
+ for file in files:
220
+ _validate_required_file_node(file, "file", _CMD)
221
+
222
+ final_files: list[File] = []
223
+ for file in files:
224
+ final_files.append(
225
+ File(fp=file.content, filename=file.filename, description=file.description, spoiler=file.spoiler))
193
226
 
194
227
  if embeds is None:
195
228
  embeds = ListNode([])
@@ -197,7 +230,7 @@ async def dshell_edit_message(ctx: Message, message, new_content=None, embeds=No
197
230
  elif isinstance(embeds, Embed):
198
231
  embeds = ListNode([embeds])
199
232
 
200
- await edit_message.edit(content=new_content, embeds=embeds, view=view)
233
+ await edit_message.edit(content=new_content, embeds=embeds, view=view, files=final_files)
201
234
 
202
235
  return edit_message.id
203
236
 
@@ -14,11 +14,13 @@ __all__ = [
14
14
  "_validate_optional_code_node",
15
15
  "_validate_optional_list_node",
16
16
  "_validate_optional_eval_group_node",
17
+ "_validate_optional_file_node",
17
18
  "_validate_required_bool",
18
19
  "_validate_required_list_node",
19
20
  "_validate_required_int",
20
21
  "_validate_required_string",
21
22
  "_validate_required_dict",
23
+ "_validate_required_file_node",
22
24
  "_validate_missing_or_type",
23
25
  "_validate_not_none",
24
26
  "_validate_has_attribute",
@@ -153,6 +155,19 @@ def _validate_optional_list_node(value, param_name: str, command_name: str):
153
155
  if value is not None and not isinstance(value, ListNode):
154
156
  raise TypeError(f"[{command_name}] -> {param_name} must be a list, not {type(value).__name__}")
155
157
 
158
+ def _validate_optional_file_node(value, param_name: str, command_name: str):
159
+ """
160
+ Validate that a optional value is a FileNode type.
161
+ :param value: The value to validate
162
+ :param param_name: The parameter name for error messages
163
+ :param command_name: The command name for error messages (optional)
164
+ :raises TypeError: If the value is not a FileNode
165
+ """
166
+ from ...DshellParser.ast_nodes import AttachmentNode
167
+
168
+ if value is not None and not isinstance(value, AttachmentNode):
169
+ raise TypeError(f"[{command_name}] -> {param_name} must be a FileNode, not {type(value).__name__}")
170
+
156
171
 
157
172
  # Required parameter validation functions
158
173
 
@@ -217,6 +232,21 @@ def _validate_required_dict(value, param_name: str, command_name: str):
217
232
  if not isinstance(value, dict):
218
233
  raise TypeError(f"[{command_name}] -> {param_name} must be a dict, not {type(value).__name__}")
219
234
 
235
+ def _validate_required_file_node(value, param_name: str, command_name: str):
236
+ """
237
+ Validate that a required value is a FileNode type.
238
+ :param value: The value to validate
239
+ :param param_name: The parameter name for error messages
240
+ :param command_name: The command name for error messages (optional)
241
+ :raises TypeError: If the value is not a FileNode
242
+ """
243
+ from ...DshellParser.ast_nodes import AttachmentNode
244
+
245
+ if not isinstance(value, AttachmentNode):
246
+ raise TypeError(f"[{command_name}] -> {param_name} must be a FileNode, not {type(value).__name__}")
247
+
248
+
249
+
220
250
 
221
251
  # Validation functions for _MissingSentinel or other types
222
252
 
@@ -1,5 +1,7 @@
1
- from Dshell.full_import import Any, randint, Optional, Union
1
+ from Dshell.full_import import (Any, randint, Optional, Union, Attachment,
2
+ HTTPException, Forbidden)
2
3
  from ..DshellTokenizer.dshell_token_type import Token
4
+ from inspect import iscoroutinefunction
3
5
 
4
6
  __all__ = [
5
7
  'ASTNode',
@@ -30,7 +32,9 @@ __all__ = [
30
32
  'UiButtonNode',
31
33
  'UiSelectNode',
32
34
  'OptionUiSelectNode',
33
- 'ScanNode'
35
+ 'ScanNode',
36
+ 'AttachmentNode',
37
+ 'FileNode'
34
38
  ]
35
39
 
36
40
 
@@ -943,3 +947,110 @@ class ListNode(ASTNode):
943
947
 
944
948
  def __repr__(self):
945
949
  return f"<LIST> - {self.iterable}"
950
+
951
+ class AttachmentNode(ASTNode):
952
+
953
+ def __init__(self, attachment: Attachment):
954
+ super().__init__(0)
955
+ self.attachment = attachment
956
+
957
+ async def read(self):
958
+ """
959
+ Lit l'intégralité du fichier et le décode en utf-8
960
+ :return:
961
+ """
962
+ try:
963
+ filebytes: bytes = await self.attachment.read()
964
+ return filebytes.decode(encoding="utf-8", errors="ignore")
965
+ except Forbidden:
966
+ return b"The file is not accessible, the bot does not have permission to access it"
967
+ except HTTPException:
968
+ return b"Download file failed"
969
+ except Exception:
970
+ return b"An error occurred in the file read action"
971
+
972
+ async def read_sector(self, sector: int):
973
+ """
974
+ Lit tout le fichier secteur par secteur en les décodant en utf-8
975
+ :return:
976
+ """
977
+ try:
978
+ async for chunk in await self.attachment.read_chunked(sector):
979
+ yield chunk.decode(encoding="utf-8", errors="ignore")
980
+ except Forbidden:
981
+ yield b"The file is not accessible, the bot does not have permission to access it"
982
+ except HTTPException:
983
+ yield b"Download file failed"
984
+ except Exception:
985
+ yield b"An error occurred in the file read action"
986
+
987
+ def content_type(self):
988
+ return self.attachment.content_type
989
+
990
+ def desc(self):
991
+ return self.attachment.description
992
+
993
+ def description(self):
994
+ return self.attachment.description
995
+
996
+ def duration(self):
997
+ return self.attachment.duration_secs
998
+
999
+ def ephemeral(self):
1000
+ return self.attachment.ephemeral
1001
+
1002
+ def url(self):
1003
+ return self.attachment.url
1004
+
1005
+ def filename(self):
1006
+ return self.attachment.filename
1007
+
1008
+ def size(self):
1009
+ return self.attachment.size
1010
+
1011
+ def title(self):
1012
+ return self.attachment.title
1013
+
1014
+ def height(self):
1015
+ return self.attachment.height
1016
+
1017
+ def width(self):
1018
+ return self.attachment.width
1019
+
1020
+ def signature(self):
1021
+ return self.attachment.hm
1022
+
1023
+ def call(self, attribute: str):
1024
+ """
1025
+ Permet d'appeler une méthode de l'attachment
1026
+ :param attribute: nom de la méthode à appeler
1027
+ :return: le résultat de la méthode appelée
1028
+ """
1029
+ if hasattr(self, attribute) and attribute != 'call':
1030
+ attr = getattr(self, attribute)
1031
+ if not iscoroutinefunction(attr) and callable(attr):
1032
+ return attr()
1033
+ else:
1034
+ raise AttributeError(f"'{attribute}' is not a callable method of the attachment !")
1035
+ else:
1036
+ raise AttributeError(f"The attachment does not have an attribute named '{attribute}' !")
1037
+
1038
+ class FileNode(ASTNode):
1039
+ def __init__(self, name: Optional[str], description: Optional[str] = None, spoiler: bool = False):
1040
+ super().__init__(0)
1041
+ self.name = name
1042
+ self.description = description
1043
+ self.spoiler = spoiler
1044
+ self.content: bytearray = bytearray("", "utf-8")
1045
+
1046
+ def write(self, content: bytearray, append: bool):
1047
+ if append:
1048
+ self.content += content
1049
+ else:
1050
+ self.content = content
1051
+
1052
+ def read(self):
1053
+ return self.content.decode(encoding="utf-8", errors="ignore")
1054
+
1055
+ def size(self):
1056
+ return len(self.content)
@@ -30,7 +30,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
30
30
  first_token_line = tokens_by_line[0] # get the first token of the line
31
31
  last_block = blocks[-1]
32
32
 
33
- line = pointer+1
33
+ line = first_token_line.position[0]
34
34
 
35
35
  if first_token_line.type == DTT.COMMAND: # if the token is a command
36
36
  body = tokens_by_line[1:] # get its arguments
@@ -42,7 +42,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
42
42
  elif first_token_line.type == DTT.KEYWORD: # if it's a keyword
43
43
 
44
44
  if first_token_line.value == 'if': # if it's a condition
45
- if len(tokens_by_line) <= 1:
45
+ if len_tokens_by_line_since_command_name <= 0:
46
46
  raise SyntaxError(f'[IF] Take one or more arguments on line {first_token_line.position} !')
47
47
 
48
48
  if_node = IfNode(condition=tokens_by_line[1:],
@@ -67,7 +67,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
67
67
  elif first_token_line.value == 'elif':
68
68
  if not isinstance(last_block, (IfNode, ElifNode)):
69
69
  raise SyntaxError(f'[ELIF] No conditional bloc open on line {first_token_line.position} !')
70
- if len(tokens_by_line) <= 1:
70
+ if len_tokens_by_line_since_command_name <= 0:
71
71
  raise SyntaxError(f'[ELIF] Take one or more arguments on line {first_token_line.position} !')
72
72
  elif_node = ElifNode(condition=tokens_by_line[1:], body=[],
73
73
  parent=last_block if isinstance(last_block, IfNode) else last_block.parent, line=line)
@@ -197,7 +197,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
197
197
  return blocks, pointer
198
198
 
199
199
  elif first_token_line.value == 'eval':
200
- if len(tokens_by_line) < 2:
200
+ if len_tokens_by_line_since_command_name < 1:
201
201
  raise SyntaxError(f"[EVAL] take one or more arguments on line {first_token_line.position}")
202
202
 
203
203
  if tokens_by_line[1].type != DTT.IDENT:
@@ -210,7 +210,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
210
210
  elif first_token_line.value == 'return':
211
211
  if not isinstance(last_block, CodeNode):
212
212
  raise SyntaxError(f"[RETURN] No code open on line {first_token_line.position} !")
213
- if len(tokens_by_line) < 2:
213
+ if len_tokens_by_line_since_command_name < 1:
214
214
  raise SyntaxError(f"[RETURN] take one or more arguments on line {first_token_line.position}")
215
215
 
216
216
  return_node = ReturnNode(body=tokens_by_line[1:], line=line)
@@ -222,7 +222,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
222
222
 
223
223
  elif first_token_line.value == '#end': # node pour arrêter le programme si elle est rencontré
224
224
  error_message = True
225
- if len(tokens_by_line) > 1:
225
+ if len_tokens_by_line_since_command_name > 0:
226
226
  if tokens_by_line[1].type != DTT.BOOL:
227
227
  raise TypeError(f'[#END] the variable given must be a boolean, not {tokens_by_line[1].type}')
228
228
  else:
@@ -235,7 +235,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
235
235
  elif first_token_line.type == DTT.DISCORD_KEYWORD:
236
236
 
237
237
  if first_token_line.value == 'embed':
238
- if len(tokens_by_line) <= 1:
238
+ if len_tokens_by_line_since_command_name <= 0:
239
239
  raise SyntaxError(f'[EMBED] Take one or more arguments on line {first_token_line.position} !')
240
240
  if tokens_by_line[1].type != DTT.IDENT:
241
241
  raise TypeError(f'[EMBED] the variable given must be a ident, '
@@ -254,7 +254,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
254
254
  return blocks, pointer
255
255
 
256
256
  elif first_token_line.value == 'field':
257
- if len(tokens_by_line) <= 1:
257
+ if len_tokens_by_line_since_command_name <= 0:
258
258
  raise SyntaxError(f'[FIELD] Take one or more arguments on line {first_token_line.position} !')
259
259
  if not isinstance(last_block, EmbedNode):
260
260
  raise SyntaxError(f'[FIELD] No embed open on line {first_token_line.position} !')
@@ -262,7 +262,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
262
262
  last_block.fields.append(FieldEmbedNode(tokens_by_line[1:], line=line))
263
263
 
264
264
  elif first_token_line.value in ('perm', 'permission'):
265
- if len(tokens_by_line) <= 1:
265
+ if len_tokens_by_line_since_command_name <= 0:
266
266
  raise SyntaxError(f'[PERM] Take one argument on line {first_token_line.position} !')
267
267
  if tokens_by_line[1].type != DTT.IDENT:
268
268
  raise TypeError(f'[PERM] the variable given must be a ident, '
@@ -281,7 +281,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
281
281
  return blocks, pointer
282
282
 
283
283
  elif first_token_line.value == 'button':
284
- if len(tokens_by_line) <= 1:
284
+ if len_tokens_by_line_since_command_name <= 0:
285
285
  raise SyntaxError(f'[BUTTON] Take one or more arguments on line {first_token_line.position} !')
286
286
  if tokens_by_line[1].type != DTT.IDENT:
287
287
  raise TypeError(f'[BUTTON] the variable given must be a ident, '
@@ -300,7 +300,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
300
300
  return blocks, pointer
301
301
 
302
302
  elif first_token_line.value == 'select':
303
- if len(tokens_by_line) <= 1:
303
+ if len_tokens_by_line_since_command_name <= 0:
304
304
  raise SyntaxError(f'[SELECT] Take one or more arguments on line {first_token_line.position} !')
305
305
  if tokens_by_line[1].type != DTT.IDENT:
306
306
  raise TypeError(f'[SELECT] the variable given must be a ident, '
@@ -319,7 +319,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
319
319
  return blocks, pointer
320
320
 
321
321
  elif first_token_line.value == 'option':
322
- if len(tokens_by_line) <= 1:
322
+ if len_tokens_by_line_since_command_name <= 0:
323
323
  raise SyntaxError(f'[OPTION] Take one or more arguments on line {first_token_line.position} !')
324
324
  if not isinstance(last_block, UiSelectNode):
325
325
  raise SyntaxError(f'[OPTION] No UISelect open on line {first_token_line.position} !')
@@ -329,10 +329,19 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
329
329
  ############################## AUTRE ##############################
330
330
 
331
331
  elif first_token_line.type in DTT_DATA: # if the line starts with a data token, we consider it as a command with an implicit "sm" name
332
- for token in tokens_by_line:
333
332
 
334
- if token.type in DTT_DATA:
335
- last_block.body.append(CommandNode(name='sm', body=ArgsCommandNode([token], line=line), line=line))
333
+ if (len_tokens_by_line_since_command_name > 0 and
334
+ tokens_by_line[1].type in (DTT.LOGIC_OPERATOR, DTT.MATHS_OPERATOR, DTT.LOGIC_WORD_OPERATOR)):
335
+
336
+ new_content = [first_token_line]
337
+ new_content.extend(tokens_by_line[1:])
338
+ last_block.body.append(VarNode(first_token_line, new_content, line=line))
339
+
340
+ else:
341
+ for token in tokens_by_line:
342
+
343
+ if token.type in DTT_DATA:
344
+ last_block.body.append(CommandNode(name='sm', body=ArgsCommandNode([token], line=line), line=line))
336
345
 
337
346
  else:
338
347
  last_block.body += tokens_by_line
@@ -11,7 +11,7 @@ __all__ = [
11
11
  from ..DISCORD_COMMANDS import *
12
12
 
13
13
  from Dshell.full_import import Callable
14
-
14
+ from ..DISCORD_COMMANDS.dshell_file import dshell_read_file, dshell_write_file, dshell_get_message_files
15
15
 
16
16
  dshell_keyword: set[str] = {
17
17
  'if', 'else', 'elif', 'loop', '#end', 'var', '#loop', '#if', 'sleep', 'param', '#param', 'code', '#code', 'eval', 'return'
@@ -81,6 +81,11 @@ dshell_commands: dict[str, Callable] = {
81
81
  ## Pastbin command
82
82
  "gp": dshell_get_pastbin, # get pastbin
83
83
 
84
+ ## Files
85
+ "rf": dshell_read_file,
86
+ "wf": dshell_write_file,
87
+ "gmfs": dshell_get_message_files,
88
+
84
89
  ## Discord commands
85
90
  "sm": dshell_send_message, # send message
86
91
  "spm": dshell_send_private_message, # send private message
@@ -201,7 +206,8 @@ dshell_logical_operators: dict[str, tuple[Callable, int, int, int]] = {
201
206
  r"<": (lambda a, b: a < b, 4, 2, 2),
202
207
  r">": (lambda a, b: a > b, 4, 2, 2),
203
208
  r"!": (lambda a: not a, 3, 1, 1),
204
- r"?": (lambda condition, first_choice, second_choice: first_choice if condition else second_choice, 1, 3, 3)
209
+ r"?": (lambda condition, first_choice, second_choice: first_choice if condition else second_choice, 1, 3, 3),
210
+ r".": (lambda target, attribute: target.call(attribute) if hasattr(target, attribute) and hasattr(target, 'call') else None, 9, 2, 2), # attribute access operator
205
211
 
206
212
  }
207
213
 
@@ -55,12 +55,13 @@ table_regex: dict[DTT, Pattern] = {
55
55
  DTT.KEYWORD: compile(rf"(?<!\w)(#?{'|'.join(dshell_keyword)})(?!\w)"),
56
56
  DTT.DISCORD_KEYWORD: compile(rf"(?<!\w|-)(#?{'|'.join(dshell_discord_keyword)})(?!\w|-)", flags=IGNORECASE),
57
57
  DTT.COMMAND: compile(rf"\b({'|'.join(dshell_commands.keys())})\b", flags=IGNORECASE),
58
- DTT.MATHS_OPERATOR: compile(rf"({'|'.join([escape(i) for i in dshell_mathematical_operators.keys()])})"),
59
- DTT.LOGIC_OPERATOR: compile(rf"({'|'.join([escape(i) for i in dshell_logical_operators.keys()])})"),
60
- DTT.LOGIC_WORD_OPERATOR: compile(rf"(?:^|\s)({'|'.join([escape(i) for i in dshell_logical_word_operators.keys()])})(?:$|\s)"),
61
58
  DTT.FLOAT: compile(r"(\d+\.\d+)"),
62
59
  DTT.HEXA: compile(r"(0[Xx][0-9a-fA-F]+)"),
63
60
  DTT.INT: compile(r"(\d+)"),
61
+ DTT.MATHS_OPERATOR: compile(rf"({'|'.join([escape(i) for i in dshell_mathematical_operators.keys()])})"),
62
+ DTT.LOGIC_OPERATOR: compile(rf"({'|'.join([escape(i) for i in dshell_logical_operators.keys()])})"),
63
+ DTT.LOGIC_WORD_OPERATOR: compile(
64
+ rf"(?:^|\s)({'|'.join([escape(i) for i in dshell_logical_word_operators.keys()])})(?:$|\s)"),
64
65
  DTT.BOOL: compile(r"(True|False)", flags=IGNORECASE),
65
66
  DTT.NONE: compile(r"(None)", flags=IGNORECASE),
66
67
  DTT.IDENT: compile(rf"([A-Za-z0-9_]+)"),
@@ -7,9 +7,7 @@ from datetime import timedelta, datetime, UTC
7
7
  from requests import get
8
8
 
9
9
  from discord.ui import Button, Select
10
- from discord import (ButtonStyle, Interaction, Guild, Member, Role, Permissions, PermissionOverwrite, Message, MISSING,
11
- CategoryChannel, VoiceChannel, TextChannel, Thread, PartialMessage, NotFound, ForumChannel, Colour,
12
- Embed, AutoShardedBot, ComponentType, AllowedMentions)
10
+ from discord import *
13
11
  from discord.abc import PrivateChannel
14
12
  from discord.utils import get, _MissingSentinel
15
13
  from contextvars import ContextVar
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dshellInterpreter
3
- Version: 1.1.4.2
3
+ Version: 1.1.4.4
4
4
  Summary: A Discord bot interpreter for creating custom commands and automations.
5
5
  Home-page: https://github.com/BOXERRMD/Dshell_Interpreter
6
6
  Author: Chronos
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dshellInterpreter
3
- Version: 1.1.4.2
3
+ Version: 1.1.4.4
4
4
  Summary: A Discord bot interpreter for creating custom commands and automations.
5
5
  Home-page: https://github.com/BOXERRMD/Dshell_Interpreter
6
6
  Author: Chronos
@@ -7,6 +7,7 @@ Dshell/regex_test.py
7
7
  Dshell/DISCORD_COMMANDS/__init__.py
8
8
  Dshell/DISCORD_COMMANDS/dshell_channel.py
9
9
  Dshell/DISCORD_COMMANDS/dshell_embed.py
10
+ Dshell/DISCORD_COMMANDS/dshell_file.py
10
11
  Dshell/DISCORD_COMMANDS/dshell_interaction.py
11
12
  Dshell/DISCORD_COMMANDS/dshell_member.py
12
13
  Dshell/DISCORD_COMMANDS/dshell_message.py
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setup(
7
7
  name="dshellInterpreter",
8
- version="1.1.4.2",
8
+ version="1.1.4.4",
9
9
  author="Chronos",
10
10
  author_email="vagabonwalybi@gmail.com",
11
11
  description="A Discord bot interpreter for creating custom commands and automations.",