dshellInterpreter 0.2.21.7__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.
- Dshell/DISCORD_COMMANDS/__init__.py +7 -0
- Dshell/DISCORD_COMMANDS/dshell_channel.py +612 -0
- Dshell/DISCORD_COMMANDS/dshell_interaction.py +89 -0
- Dshell/DISCORD_COMMANDS/dshell_member.py +274 -0
- Dshell/DISCORD_COMMANDS/dshell_message.py +444 -0
- Dshell/DISCORD_COMMANDS/dshell_pastbin.py +28 -0
- Dshell/DISCORD_COMMANDS/dshell_role.py +128 -0
- Dshell/DISCORD_COMMANDS/utils/__init__.py +8 -0
- Dshell/DISCORD_COMMANDS/utils/utils_global.py +155 -0
- Dshell/DISCORD_COMMANDS/utils/utils_list.py +109 -0
- Dshell/DISCORD_COMMANDS/utils/utils_member.py +30 -0
- Dshell/DISCORD_COMMANDS/utils/utils_message.py +78 -0
- Dshell/DISCORD_COMMANDS/utils/utils_permissions.py +94 -0
- Dshell/DISCORD_COMMANDS/utils/utils_string.py +157 -0
- Dshell/DISCORD_COMMANDS/utils/utils_thread.py +35 -0
- Dshell/_DshellInterpreteur/__init__.py +4 -0
- Dshell/_DshellInterpreteur/cached_messages.py +4 -0
- Dshell/_DshellInterpreteur/dshell_arguments.py +74 -0
- Dshell/_DshellInterpreteur/dshell_interpreter.py +671 -0
- Dshell/_DshellInterpreteur/errors.py +8 -0
- Dshell/_DshellParser/__init__.py +2 -0
- Dshell/_DshellParser/ast_nodes.py +675 -0
- Dshell/_DshellParser/dshell_parser.py +408 -0
- Dshell/_DshellTokenizer/__init__.py +4 -0
- Dshell/_DshellTokenizer/dshell_keywords.py +193 -0
- Dshell/_DshellTokenizer/dshell_token_type.py +58 -0
- Dshell/_DshellTokenizer/dshell_tokenizer.py +146 -0
- Dshell/__init__.py +3 -0
- Dshell/_utils.py +1 -0
- dshellinterpreter-0.2.21.7.dist-info/METADATA +37 -0
- dshellinterpreter-0.2.21.7.dist-info/RECORD +34 -0
- dshellinterpreter-0.2.21.7.dist-info/WHEEL +5 -0
- dshellinterpreter-0.2.21.7.dist-info/licenses/LICENSE +21 -0
- dshellinterpreter-0.2.21.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
"utils_len",
|
|
3
|
+
"utils_random",
|
|
4
|
+
"utils_get_name",
|
|
5
|
+
"utils_get_id",
|
|
6
|
+
"utils_get_roles",
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
from discord import Message, Role, TextChannel, VoiceChannel, CategoryChannel, ForumChannel, Thread, Member, Guild
|
|
10
|
+
from discord.utils import get
|
|
11
|
+
from typing import Union, Optional
|
|
12
|
+
from enum import StrEnum
|
|
13
|
+
from random import random, choice
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DiscordType(StrEnum):
|
|
17
|
+
MEMBER = "member"
|
|
18
|
+
ROLE = "role"
|
|
19
|
+
TEXT_CHANNEL = "text_channel"
|
|
20
|
+
VOICE_CHANNEL = "voice_channel"
|
|
21
|
+
CATEGORY_CHANNEL = "category_channel"
|
|
22
|
+
FORUM_CHANNEL = "forum_channel"
|
|
23
|
+
THREAD = "thread"
|
|
24
|
+
UNKNOWN = "unknown"
|
|
25
|
+
|
|
26
|
+
def utils_what_discord_type_is(ctx: Union[Message, Guild], value: int) -> tuple[str, Union[Member, Role, TextChannel, VoiceChannel, CategoryChannel, ForumChannel, Thread, None]]:
|
|
27
|
+
"""
|
|
28
|
+
Return an enum of what the value is (str, int, list, Member, Role, Channel, etc.)
|
|
29
|
+
:param ctx:
|
|
30
|
+
:param value:
|
|
31
|
+
:return:
|
|
32
|
+
"""
|
|
33
|
+
guild = ctx if isinstance(ctx, Guild) else ctx.guild
|
|
34
|
+
|
|
35
|
+
if member := guild.get_member(value):
|
|
36
|
+
return DiscordType.MEMBER, member
|
|
37
|
+
|
|
38
|
+
elif role := guild.get_role(value):
|
|
39
|
+
return DiscordType.ROLE, role
|
|
40
|
+
|
|
41
|
+
elif (channel := guild.get_channel(value)) and isinstance(channel, TextChannel):
|
|
42
|
+
return DiscordType.TEXT_CHANNEL, channel
|
|
43
|
+
|
|
44
|
+
elif (channel := guild.get_channel(value)) and isinstance(channel, VoiceChannel):
|
|
45
|
+
return DiscordType.VOICE_CHANNEL, channel
|
|
46
|
+
|
|
47
|
+
elif (channel := guild.get_channel(value)) and isinstance(channel, CategoryChannel):
|
|
48
|
+
return DiscordType.CATEGORY_CHANNEL, channel
|
|
49
|
+
|
|
50
|
+
elif (channel := guild.get_channel(value)) and isinstance(channel, ForumChannel):
|
|
51
|
+
return DiscordType.FORUM_CHANNEL, channel
|
|
52
|
+
|
|
53
|
+
elif (channel := guild.get_channel(value)) and isinstance(channel, Thread):
|
|
54
|
+
return DiscordType.THREAD, channel
|
|
55
|
+
else:
|
|
56
|
+
return DiscordType.UNKNOWN, None
|
|
57
|
+
|
|
58
|
+
async def utils_len(ctx: Message, value):
|
|
59
|
+
"""
|
|
60
|
+
Return the length of a list, or a string
|
|
61
|
+
:param value:
|
|
62
|
+
:return:
|
|
63
|
+
"""
|
|
64
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
65
|
+
if not isinstance(value, (str, ListNode)):
|
|
66
|
+
raise TypeError(f"value must be a list or a string in len command, not {type(value)}")
|
|
67
|
+
|
|
68
|
+
return len(value)
|
|
69
|
+
|
|
70
|
+
async def utils_random(ctx: Message, value: Optional["ListNode"] = None):
|
|
71
|
+
"""
|
|
72
|
+
Return a random element from a list, or a random integer between 0 and value
|
|
73
|
+
:param value:
|
|
74
|
+
:return:
|
|
75
|
+
"""
|
|
76
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
77
|
+
if value is not None and not isinstance(value, ListNode):
|
|
78
|
+
raise TypeError(f"value must be a list in random command, not {type(value)}")
|
|
79
|
+
|
|
80
|
+
if value is None:
|
|
81
|
+
return random()
|
|
82
|
+
return choice(value)
|
|
83
|
+
|
|
84
|
+
async def utils_get_name(ctx : Message, value: int) -> Union[str, None]:
|
|
85
|
+
"""
|
|
86
|
+
Return the name of a role, member, or channel.
|
|
87
|
+
If not found, return None.
|
|
88
|
+
:param value:
|
|
89
|
+
:return:
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
if not isinstance(value, int):
|
|
93
|
+
raise TypeError(f"value must be an int in name command, not {type(value)}")
|
|
94
|
+
|
|
95
|
+
guild = ctx.guild
|
|
96
|
+
result = None
|
|
97
|
+
|
|
98
|
+
if role := guild.get_role(value):
|
|
99
|
+
result = role.name
|
|
100
|
+
|
|
101
|
+
elif member := guild.get_member(value):
|
|
102
|
+
result = member.name
|
|
103
|
+
|
|
104
|
+
if channel := guild.get_channel(value) :
|
|
105
|
+
result = channel.name
|
|
106
|
+
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
async def utils_get_id(ctx : Message, value: str) -> Union[int, None]:
|
|
110
|
+
"""
|
|
111
|
+
Return the id of a role, member, or channel.
|
|
112
|
+
If not found, return None.
|
|
113
|
+
:param value:
|
|
114
|
+
:return:
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
if not isinstance(value, str):
|
|
118
|
+
raise TypeError(f"value must be a str in id command, not {type(value)}")
|
|
119
|
+
|
|
120
|
+
guild = ctx.guild
|
|
121
|
+
result = None
|
|
122
|
+
|
|
123
|
+
if role := get(guild.roles, name=value):
|
|
124
|
+
result = role.id
|
|
125
|
+
|
|
126
|
+
elif member := get(guild.members, name=value):
|
|
127
|
+
result = member.id
|
|
128
|
+
|
|
129
|
+
if channel := get(guild.channels, name=value) :
|
|
130
|
+
result = channel.id
|
|
131
|
+
|
|
132
|
+
return result
|
|
133
|
+
|
|
134
|
+
async def utils_get_roles(ctx: Message, value: int):
|
|
135
|
+
"""
|
|
136
|
+
Return the roles of a member.
|
|
137
|
+
:param value:
|
|
138
|
+
:return:
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
if not isinstance(value, int):
|
|
142
|
+
raise TypeError(f"value must be an int in roles command, not {type(value)}")
|
|
143
|
+
|
|
144
|
+
guild = ctx.guild
|
|
145
|
+
|
|
146
|
+
what_is, member = utils_what_discord_type_is(ctx, value)
|
|
147
|
+
|
|
148
|
+
if what_is == DiscordType.UNKNOWN:
|
|
149
|
+
raise ValueError(f"{value} member not found in guild {guild.name}")
|
|
150
|
+
|
|
151
|
+
if what_is != DiscordType.MEMBER:
|
|
152
|
+
raise TypeError(f"value must be a member id in roles command, not {what_is}")
|
|
153
|
+
|
|
154
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
155
|
+
return ListNode([i.id for i in member.roles])
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
"utils_list_add",
|
|
3
|
+
"utils_list_remove",
|
|
4
|
+
"utils_list_clear",
|
|
5
|
+
"utils_list_pop",
|
|
6
|
+
"utils_list_sort",
|
|
7
|
+
"utils_list_reverse",
|
|
8
|
+
"utils_list_get_value",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
async def utils_list_add(ctx, value: "ListNode", *elements):
|
|
12
|
+
"""
|
|
13
|
+
Add an element to a list
|
|
14
|
+
:param value:
|
|
15
|
+
:param elements:
|
|
16
|
+
:return:
|
|
17
|
+
"""
|
|
18
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
19
|
+
if not isinstance(value, ListNode):
|
|
20
|
+
raise TypeError("value must be a list in add command")
|
|
21
|
+
|
|
22
|
+
for elem in elements:
|
|
23
|
+
if isinstance(elem, ListNode):
|
|
24
|
+
value.extend(elem)
|
|
25
|
+
else:
|
|
26
|
+
value.add(elem)
|
|
27
|
+
return value
|
|
28
|
+
|
|
29
|
+
async def utils_list_remove(ctx, value: "ListNode", element, count: int = 1):
|
|
30
|
+
"""
|
|
31
|
+
Remove an element from a list
|
|
32
|
+
:param value:
|
|
33
|
+
:param element:
|
|
34
|
+
:param count:
|
|
35
|
+
:return:
|
|
36
|
+
"""
|
|
37
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
38
|
+
if not isinstance(value, ListNode):
|
|
39
|
+
raise TypeError("value must be a list in remove command")
|
|
40
|
+
|
|
41
|
+
value.remove(element, count)
|
|
42
|
+
return value
|
|
43
|
+
|
|
44
|
+
async def utils_list_clear(ctx, value: "ListNode"):
|
|
45
|
+
"""
|
|
46
|
+
Clear a list
|
|
47
|
+
:param value:
|
|
48
|
+
:return:
|
|
49
|
+
"""
|
|
50
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
51
|
+
if not isinstance(value, ListNode):
|
|
52
|
+
raise TypeError("value must be a list in clear command")
|
|
53
|
+
value.clear()
|
|
54
|
+
return value
|
|
55
|
+
|
|
56
|
+
async def utils_list_pop(ctx, value: "ListNode", index: int = -1):
|
|
57
|
+
"""
|
|
58
|
+
Pop an element from a list
|
|
59
|
+
:param value:
|
|
60
|
+
:param index:
|
|
61
|
+
:return:
|
|
62
|
+
"""
|
|
63
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
64
|
+
if not isinstance(value, ListNode):
|
|
65
|
+
raise TypeError("value must be a list in pop command")
|
|
66
|
+
if not isinstance(index, int):
|
|
67
|
+
raise TypeError("index must be an integer in pop command")
|
|
68
|
+
return value.pop(index)
|
|
69
|
+
|
|
70
|
+
async def utils_list_sort(ctx, value: "ListNode", reverse: bool = False):
|
|
71
|
+
"""
|
|
72
|
+
Sort a list
|
|
73
|
+
:param value:
|
|
74
|
+
:param reverse:
|
|
75
|
+
:return:
|
|
76
|
+
"""
|
|
77
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
78
|
+
if not isinstance(value, ListNode):
|
|
79
|
+
raise TypeError("value must be a list in sort command")
|
|
80
|
+
if not isinstance(reverse, bool):
|
|
81
|
+
raise TypeError("reverse must be a boolean in sort command")
|
|
82
|
+
value.sort(reverse=reverse)
|
|
83
|
+
return value
|
|
84
|
+
|
|
85
|
+
async def utils_list_reverse(ctx, value: "ListNode"):
|
|
86
|
+
"""
|
|
87
|
+
Reverse a list
|
|
88
|
+
:param value:
|
|
89
|
+
:return:
|
|
90
|
+
"""
|
|
91
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
92
|
+
if not isinstance(value, ListNode):
|
|
93
|
+
raise TypeError("value must be a list in reverse command")
|
|
94
|
+
value.reverse()
|
|
95
|
+
return value
|
|
96
|
+
|
|
97
|
+
async def utils_list_get_value(ctx, value: "ListNode", index: int = 0):
|
|
98
|
+
"""
|
|
99
|
+
Get a value from a list
|
|
100
|
+
:param value:
|
|
101
|
+
:param index:
|
|
102
|
+
:return:
|
|
103
|
+
"""
|
|
104
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
105
|
+
if not isinstance(value, ListNode):
|
|
106
|
+
raise TypeError("value must be a list in get command")
|
|
107
|
+
if not isinstance(index, int):
|
|
108
|
+
raise TypeError("index must be an integer in get command")
|
|
109
|
+
return value[index]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
"utils_has_permissions",
|
|
3
|
+
]
|
|
4
|
+
from discord import Message, PermissionOverwrite
|
|
5
|
+
|
|
6
|
+
from .utils_global import utils_what_discord_type_is, DiscordType
|
|
7
|
+
|
|
8
|
+
async def utils_has_permissions(ctx: Message, member: int, permission: dict[None, PermissionOverwrite]) -> bool:
|
|
9
|
+
"""
|
|
10
|
+
Return True if the member has the specified permissions.
|
|
11
|
+
:param member:
|
|
12
|
+
:param permission:
|
|
13
|
+
:return:
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
if not isinstance(member, int):
|
|
17
|
+
raise TypeError(f"member must be an int in has_perms command, not {type(member)}")
|
|
18
|
+
|
|
19
|
+
if not isinstance(permission, dict):
|
|
20
|
+
raise TypeError(f"permissions must be a permission bloc in has_perms command, not {type(permission)}")
|
|
21
|
+
|
|
22
|
+
if None not in permission:
|
|
23
|
+
raise ValueError(f"permissions must have simple 'allow' permission in has_perms command, not {permission.keys()}")
|
|
24
|
+
|
|
25
|
+
discord_type, member = utils_what_discord_type_is(ctx, member)
|
|
26
|
+
|
|
27
|
+
if discord_type != DiscordType.MEMBER:
|
|
28
|
+
raise ValueError(f"No member found with ID {member} in has_perms command.")
|
|
29
|
+
|
|
30
|
+
return (member.guild_permissions & permission[None].pair()[0]) == permission[None].pair()[0]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from discord import Message, PartialMessage, AllowedMentions
|
|
2
|
+
from typing import Union
|
|
3
|
+
from re import search
|
|
4
|
+
|
|
5
|
+
from ..._DshellInterpreteur.cached_messages import dshell_cached_messages
|
|
6
|
+
|
|
7
|
+
def utils_get_message(ctx: Message, message: Union[int, str]) -> Union[PartialMessage, Message]:
|
|
8
|
+
"""
|
|
9
|
+
Returns the message object of the specified message ID or link.
|
|
10
|
+
Message is only available in the same server as the command and in the same channel.
|
|
11
|
+
If the message is a link, it must be in the format: https://discord.com/channels/{guild_id}/{channel_id}/{message_id}
|
|
12
|
+
"""
|
|
13
|
+
cached_messages = dshell_cached_messages.get()
|
|
14
|
+
|
|
15
|
+
if isinstance(message, int):
|
|
16
|
+
if message in cached_messages:
|
|
17
|
+
return cached_messages[message]
|
|
18
|
+
|
|
19
|
+
cached_messages[message] = ctx.channel.get_partial_message(message)
|
|
20
|
+
dshell_cached_messages.set(cached_messages)
|
|
21
|
+
return cached_messages[message]
|
|
22
|
+
|
|
23
|
+
elif isinstance(message, str):
|
|
24
|
+
match = search(r'https://discord\.com/channels/(\d+)/(\d+)/(\d+)', message)
|
|
25
|
+
if not match:
|
|
26
|
+
raise Exception("Invalid message link format. Use a valid Discord message link.")
|
|
27
|
+
guild_id = int(match.group(1))
|
|
28
|
+
channel_id = int(match.group(2))
|
|
29
|
+
message_id = int(match.group(3))
|
|
30
|
+
|
|
31
|
+
if guild_id != ctx.guild.id:
|
|
32
|
+
raise Exception("The message must be from the same server as the command !")
|
|
33
|
+
|
|
34
|
+
cached_messages[message_id] = ctx.guild.get_channel(channel_id).get_partial_message(message_id)
|
|
35
|
+
dshell_cached_messages.set(cached_messages)
|
|
36
|
+
return cached_messages[message_id]
|
|
37
|
+
|
|
38
|
+
raise Exception(f"Message must be an integer or a string, not {type(message)} !")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def utils_autorised_mentions(global_mentions: bool = None,
|
|
42
|
+
everyone_mention: bool = True,
|
|
43
|
+
roles_mentions: bool = True,
|
|
44
|
+
users_mentions: bool = True,
|
|
45
|
+
reply_mention: bool = False) -> Union[bool, 'AllowedMentions']:
|
|
46
|
+
"""
|
|
47
|
+
Returns the AllowedMentions object based on the provided parameters.
|
|
48
|
+
If global_mentions is set to True or False, it overrides all other parameters.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
from discord import AllowedMentions
|
|
52
|
+
|
|
53
|
+
if global_mentions is not None and not isinstance(global_mentions, bool):
|
|
54
|
+
raise Exception(f'Mention parameter must be a boolean or None, not {type(global_mentions)} !')
|
|
55
|
+
|
|
56
|
+
if not isinstance(everyone_mention, bool):
|
|
57
|
+
raise Exception(f'Everyone mention parameter must be a boolean, not {type(everyone_mention)} !')
|
|
58
|
+
|
|
59
|
+
if not isinstance(roles_mentions, bool):
|
|
60
|
+
raise Exception(f'Roles mention parameter must be a boolean, not {type(roles_mentions)} !')
|
|
61
|
+
|
|
62
|
+
if not isinstance(users_mentions, bool):
|
|
63
|
+
raise Exception(f'Users mention parameter must be a boolean, not {type(users_mentions)} !')
|
|
64
|
+
|
|
65
|
+
if not isinstance(reply_mention, bool):
|
|
66
|
+
raise Exception(f'Reply mention parameter must be a boolean, not {type(reply_mention)} !')
|
|
67
|
+
|
|
68
|
+
if global_mentions is True:
|
|
69
|
+
return AllowedMentions.all()
|
|
70
|
+
|
|
71
|
+
elif global_mentions is False:
|
|
72
|
+
return AllowedMentions.none()
|
|
73
|
+
|
|
74
|
+
else:
|
|
75
|
+
return AllowedMentions(everyone=everyone_mention,
|
|
76
|
+
roles=roles_mentions,
|
|
77
|
+
users=users_mentions,
|
|
78
|
+
replied_user=reply_mention)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
from discord import Guild, Member, Role, Permissions, PermissionOverwrite, Message
|
|
3
|
+
|
|
4
|
+
from .utils_global import utils_what_discord_type_is, DiscordType
|
|
5
|
+
|
|
6
|
+
async def utils_update_permissions(ctx: Message,
|
|
7
|
+
permission1: dict[Union[Member, Role, None], PermissionOverwrite],
|
|
8
|
+
permission2: dict[Union[Member, Role, None], PermissionOverwrite]) -> dict:
|
|
9
|
+
|
|
10
|
+
if not isinstance(permission1, dict):
|
|
11
|
+
raise ValueError(f"permission1 must be a permission block, not {type(permission1).__name__}")
|
|
12
|
+
|
|
13
|
+
if not isinstance(permission2, dict):
|
|
14
|
+
raise ValueError(f"permission2 must be a permission block, not {type(permission2).__name__}")
|
|
15
|
+
|
|
16
|
+
permission1.update(permission2)
|
|
17
|
+
|
|
18
|
+
return permission1
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DshellPermissions:
|
|
22
|
+
def __init__(self, target: dict[str, list[int]]):
|
|
23
|
+
"""
|
|
24
|
+
Creates a Dshell permissions object.
|
|
25
|
+
:param target: A dictionary containing parameters and their values.
|
|
26
|
+
Expected parameters: “allow”, “deny”, ‘members’, “roles”.
|
|
27
|
+
For “members” and “roles”, values must be ID ListNodes.
|
|
28
|
+
"""
|
|
29
|
+
self.target: dict[str, Union["ListNode", int]] = target
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def get_member(guild, member_id: int) -> Member:
|
|
33
|
+
"""
|
|
34
|
+
Return a Member object from a member ID.
|
|
35
|
+
:param member_id:
|
|
36
|
+
:return:
|
|
37
|
+
"""
|
|
38
|
+
discord_type, instance = utils_what_discord_type_is(guild, member_id)
|
|
39
|
+
if discord_type == DiscordType.MEMBER:
|
|
40
|
+
return instance
|
|
41
|
+
raise ValueError(f"No member found with ID {member_id} in perm command.")
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def get_role(guild, role_id: int) -> Role:
|
|
45
|
+
"""
|
|
46
|
+
Return a Role object from a role ID.
|
|
47
|
+
:param role_id:
|
|
48
|
+
:return:
|
|
49
|
+
"""
|
|
50
|
+
discord_type, instance = utils_what_discord_type_is(guild, role_id)
|
|
51
|
+
if discord_type == DiscordType.ROLE:
|
|
52
|
+
return instance
|
|
53
|
+
raise ValueError(f"No role found with ID {role_id} in perm command.")
|
|
54
|
+
|
|
55
|
+
def get_permission_overwrite(self, guild: Guild) -> dict[Union[Member, Role, None], PermissionOverwrite]:
|
|
56
|
+
"""
|
|
57
|
+
Returns a PermissionOverwrite object with member and role permissions.
|
|
58
|
+
If no members or roles are specified, it returns a PermissionOverwrite with None key.
|
|
59
|
+
:param guild: The Discord server
|
|
60
|
+
:return: A dictionary of PermissionOverwrite objects with members and roles as keys
|
|
61
|
+
"""
|
|
62
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
63
|
+
permissions: dict[Union[Member, Role, None], PermissionOverwrite] = {}
|
|
64
|
+
target_keys = self.target.keys()
|
|
65
|
+
|
|
66
|
+
if 'members' in target_keys:
|
|
67
|
+
for member_id in (
|
|
68
|
+
self.target['members'] if isinstance(self.target['members'], ListNode) else [
|
|
69
|
+
self.target['members']]): # allow a single ID
|
|
70
|
+
member = self.get_member(guild, member_id)
|
|
71
|
+
permissions[member] = PermissionOverwrite.from_pair(
|
|
72
|
+
allow=Permissions(permissions=self.target.get('allow', 0)),
|
|
73
|
+
deny=Permissions(permissions=self.target.get('deny', 0))
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
elif 'roles' in target_keys:
|
|
77
|
+
for role_id in (
|
|
78
|
+
self.target['roles'] if isinstance(self.target['roles'], ListNode) else [
|
|
79
|
+
self.target['roles']]): # allow a single ID
|
|
80
|
+
if role_id == guild.id: # @everyone role
|
|
81
|
+
role = guild.default_role
|
|
82
|
+
else:
|
|
83
|
+
role = self.get_role(guild, role_id)
|
|
84
|
+
permissions[role] = PermissionOverwrite.from_pair(
|
|
85
|
+
allow=Permissions(permissions=self.target.get('allow', 0)),
|
|
86
|
+
deny=Permissions(permissions=self.target.get('deny', 0))
|
|
87
|
+
)
|
|
88
|
+
else:
|
|
89
|
+
permissions[None] = PermissionOverwrite.from_pair(
|
|
90
|
+
allow=Permissions(permissions=self.target.get('allow', 0)),
|
|
91
|
+
deny=Permissions(permissions=self.target.get('deny', 0))
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return permissions
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
"utils_split_string",
|
|
3
|
+
"utils_upper_string",
|
|
4
|
+
"utils_lower_string",
|
|
5
|
+
"utils_title_string",
|
|
6
|
+
"utils_strip_string",
|
|
7
|
+
"utils_replace_string",
|
|
8
|
+
"utils_regex_findall",
|
|
9
|
+
"utils_regex_sub",
|
|
10
|
+
"utils_regex_search"
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
from discord import Message
|
|
14
|
+
from re import search, findall, sub
|
|
15
|
+
|
|
16
|
+
async def utils_split_string(ctx: Message, value: str, separator: str = ' ') -> "ListNode":
|
|
17
|
+
"""
|
|
18
|
+
Split a string into a list of strings using the specified separator.
|
|
19
|
+
:param value:
|
|
20
|
+
:param separator:
|
|
21
|
+
:return:
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
if not isinstance(value, str):
|
|
25
|
+
raise TypeError(f"value must be a str in split command, not {type(value)}")
|
|
26
|
+
|
|
27
|
+
if not isinstance(separator, str):
|
|
28
|
+
raise TypeError(f"separator must be a str in split command, not {type(separator)}")
|
|
29
|
+
|
|
30
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
31
|
+
|
|
32
|
+
return ListNode(value.split(separator))
|
|
33
|
+
|
|
34
|
+
async def utils_upper_string(ctx: Message, value: str) -> str:
|
|
35
|
+
"""
|
|
36
|
+
Convert a string to uppercase.
|
|
37
|
+
:param value:
|
|
38
|
+
:return:
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
if not isinstance(value, str):
|
|
42
|
+
raise TypeError(f"value must be a str in upper command, not {type(value)}")
|
|
43
|
+
|
|
44
|
+
return value.upper()
|
|
45
|
+
|
|
46
|
+
async def utils_lower_string(ctx: Message, value: str) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Convert a string to lowercase.
|
|
49
|
+
:param value:
|
|
50
|
+
:return:
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
if not isinstance(value, str):
|
|
54
|
+
raise TypeError(f"value must be a str in lower command, not {type(value)}")
|
|
55
|
+
|
|
56
|
+
return value.lower()
|
|
57
|
+
|
|
58
|
+
async def utils_title_string(ctx: Message, value: str) -> str:
|
|
59
|
+
"""
|
|
60
|
+
Convert a string to title case.
|
|
61
|
+
:param value:
|
|
62
|
+
:return:
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
if not isinstance(value, str):
|
|
66
|
+
raise TypeError(f"value must be a str in title command, not {type(value)}")
|
|
67
|
+
|
|
68
|
+
return value.title()
|
|
69
|
+
|
|
70
|
+
async def utils_strip_string(ctx: Message, value: str) -> str:
|
|
71
|
+
"""
|
|
72
|
+
Strip whitespace from the beginning and end of a string.
|
|
73
|
+
:param value:
|
|
74
|
+
:return:
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
if not isinstance(value, str):
|
|
78
|
+
raise TypeError(f"value must be a str in strip command, not {type(value)}")
|
|
79
|
+
|
|
80
|
+
return value.strip()
|
|
81
|
+
|
|
82
|
+
async def utils_replace_string(ctx: Message, value: str, old: str, new: str) -> str:
|
|
83
|
+
"""
|
|
84
|
+
Replace all occurrences of old with new in a string.
|
|
85
|
+
:param value:
|
|
86
|
+
:param old:
|
|
87
|
+
:param new:
|
|
88
|
+
:return:
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
if not isinstance(value, str):
|
|
92
|
+
raise TypeError(f"value must be a str in replace command, not {type(value)}")
|
|
93
|
+
|
|
94
|
+
if not isinstance(old, str):
|
|
95
|
+
raise TypeError(f"old must be a str in replace command, not {type(old)}")
|
|
96
|
+
|
|
97
|
+
if not isinstance(new, str):
|
|
98
|
+
raise TypeError(f"new must be a str in replace command, not {type(new)}")
|
|
99
|
+
|
|
100
|
+
return value.replace(old, new)
|
|
101
|
+
|
|
102
|
+
async def utils_regex_findall(ctx: Message, regex: str, content: str = None) -> "ListNode":
|
|
103
|
+
"""
|
|
104
|
+
Find all occurrences of a regex in a string.
|
|
105
|
+
:param regex:
|
|
106
|
+
:param content:
|
|
107
|
+
:return:
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
if not isinstance(regex, str):
|
|
111
|
+
raise Exception(f"Regex must be a string, not {type(regex)}!")
|
|
112
|
+
|
|
113
|
+
if content is not None and not isinstance(content, str):
|
|
114
|
+
raise Exception(f"Content must be a string, not {type(content)}!")
|
|
115
|
+
|
|
116
|
+
from ..._DshellParser.ast_nodes import ListNode
|
|
117
|
+
|
|
118
|
+
return ListNode(findall(regex, content if content is not None else ctx.content))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async def utils_regex_sub(ctx: Message, regex: str, replace: str, content: str = None) -> str:
|
|
122
|
+
"""
|
|
123
|
+
Replace all occurrences of a regex in a string with a replacement string.
|
|
124
|
+
:param regex:
|
|
125
|
+
:param replace:
|
|
126
|
+
:param content:
|
|
127
|
+
:return:
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
if not isinstance(regex, str):
|
|
131
|
+
raise Exception(f"Regex must be a string, not {type(regex)}!")
|
|
132
|
+
|
|
133
|
+
if not isinstance(replace, str):
|
|
134
|
+
raise Exception(f"Replacement must be a string, not {type(replace)}!")
|
|
135
|
+
|
|
136
|
+
if content is not None and not isinstance(content, str):
|
|
137
|
+
raise Exception(f"Content must be a string, not {type(content)}!")
|
|
138
|
+
|
|
139
|
+
return sub(regex, replace, content if content is not None else ctx.content)
|
|
140
|
+
|
|
141
|
+
async def utils_regex_search(ctx: Message, regex: str, content: str = None) -> str:
|
|
142
|
+
"""
|
|
143
|
+
Search for a regex in a string.
|
|
144
|
+
:param regex:
|
|
145
|
+
:param content:
|
|
146
|
+
:return:
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
if not isinstance(regex, str):
|
|
150
|
+
raise Exception(f"Regex must be a string, not {type(regex)}!")
|
|
151
|
+
|
|
152
|
+
if content is not None and not isinstance(content, str):
|
|
153
|
+
raise Exception(f"Content must be a string, not {type(content)}!")
|
|
154
|
+
|
|
155
|
+
result = search(regex, content if content is not None else ctx.content)
|
|
156
|
+
|
|
157
|
+
return result.group() if result else ''
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from discord import Message, Thread
|
|
2
|
+
from discord.errors import NotFound
|
|
3
|
+
|
|
4
|
+
from typing import Union
|
|
5
|
+
from re import search
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def utils_get_thread(ctx: Message, thread: Union[int, str]) -> Thread:
|
|
9
|
+
"""
|
|
10
|
+
Returns the thread object of the specified thread ID or link.
|
|
11
|
+
Thread is only available in the same server as the command and in the same channel.
|
|
12
|
+
If the thread is a link, it must be in the format: https://discord.com/channels/{guild_id}/{channel_id}/{message_id}
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
if isinstance(thread, int):
|
|
16
|
+
return ctx.channel.get_thread(thread)
|
|
17
|
+
|
|
18
|
+
elif isinstance(thread, str):
|
|
19
|
+
match = search(r'https://discord\.com/channels/(\d+)/(\d+)(/\d+)?', thread)
|
|
20
|
+
if not match:
|
|
21
|
+
raise Exception("Invalid thread link format. Use a valid Discord thread link.")
|
|
22
|
+
guild_id = int(match.group(1))
|
|
23
|
+
message_id = int(match.group(2))
|
|
24
|
+
channel_id = ctx.channel.id if len(match.groups()) == 3 else ctx.channel.id
|
|
25
|
+
|
|
26
|
+
if guild_id != ctx.guild.id:
|
|
27
|
+
raise Exception("The thread must be from the same server as the command !")
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
c = await ctx.guild.get_channel(channel_id).fetch_message(message_id)
|
|
31
|
+
return c.thread
|
|
32
|
+
except NotFound:
|
|
33
|
+
raise Exception(f"Thread with ID {message_id} not found in channel {channel_id} !")
|
|
34
|
+
|
|
35
|
+
raise Exception(f"Thread must be an integer or a string, not {type(thread)} !")
|