PycordViews 1.1.3__py3-none-any.whl → 1.2.0__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.
- pycordViews/__init__.py +2 -2
- pycordViews/menu/__init__.py +1 -1
- pycordViews/menu/menu.py +75 -14
- pycordViews/menu/selectMenu.py +29 -9
- pycordViews/multibot/__init__.py +1 -5
- pycordViews/multibot/bot.py +194 -0
- pycordViews/multibot/errors.py +25 -5
- pycordViews/multibot/multibot.py +206 -0
- pycordViews/multibot/process.py +217 -0
- pycordViews/pagination/pagination_view.py +7 -0
- pycordViews/views/easy_modified_view.py +40 -18
- pycordviews-1.2.0.dist-info/METADATA +241 -0
- pycordviews-1.2.0.dist-info/RECORD +21 -0
- {PycordViews-1.1.3.dist-info → pycordviews-1.2.0.dist-info}/WHEEL +1 -1
- PycordViews-1.1.3.dist-info/METADATA +0 -80
- PycordViews-1.1.3.dist-info/RECORD +0 -23
- pycordViews/multibot/process_for_bots.py +0 -112
- pycordViews/multibot/process_messages.py +0 -7
- pycordViews/multibot/runner.py +0 -14
- pycordViews/multibot/start_multibot.py +0 -80
- pycordViews/typeViews.py +0 -4
- {PycordViews-1.1.3.dist-info → pycordviews-1.2.0.dist-info/licenses}/LICENSE +0 -0
- {PycordViews-1.1.3.dist-info → pycordviews-1.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,217 @@
|
|
1
|
+
from multiprocessing import Queue
|
2
|
+
from .errors import BotAlreadyExistError, BotNotFoundError, MultibotError, BotNotStartedError
|
3
|
+
from .bot import DiscordBot
|
4
|
+
from discord import Intents
|
5
|
+
from immutableType import Str_
|
6
|
+
|
7
|
+
class ManageProcess:
|
8
|
+
|
9
|
+
def __init__(self, main_queue: Queue, process_queue: Queue):
|
10
|
+
"""
|
11
|
+
Gère tous les bots dans un processus
|
12
|
+
"""
|
13
|
+
self.__bots: dict[str, DiscordBot] = {}
|
14
|
+
self.main_queue: Queue = main_queue
|
15
|
+
self.process_queue: Queue = process_queue
|
16
|
+
|
17
|
+
self.commandes = {
|
18
|
+
"ADD": self.add_bot_to_process,
|
19
|
+
"REMOVE": self.remove_bot_to_process,
|
20
|
+
"START": self.start_bot_to_process,
|
21
|
+
"STOP": self.stop_bot_to_process,
|
22
|
+
"IS_STARTED": self.is_started,
|
23
|
+
"IS_READY": self.is_ready,
|
24
|
+
"IS_WS_RATELIMITED": self.is_ws_ratelimited,
|
25
|
+
"STOPALL": self.stop_all_bot_to_process,
|
26
|
+
"STARTALL": self.start_all_bot_to_process,
|
27
|
+
"BOT_COUNT": self.bot_count,
|
28
|
+
"STARTED_BOT_COUNT": self.started_bot_count,
|
29
|
+
"SHUTDOWN_BOT_COUNT": self.shutdown_bot_count,
|
30
|
+
"BOTS_bot_name": self.get_bots_bot_name,
|
31
|
+
"RELOAD_COMMANDS": self.reload_all_commands,
|
32
|
+
"ADD_COMMAND_FILE": self.add_pyFile_commands,
|
33
|
+
"MODIFY_COMMAND_FILE": self.modify_pyFile_commands
|
34
|
+
}
|
35
|
+
|
36
|
+
def run(self):
|
37
|
+
"""
|
38
|
+
Boucle principale du processus, écoute la queue principale.
|
39
|
+
Doit comporter aubligatoirement un dictionnaire avec la clé 'type'
|
40
|
+
"""
|
41
|
+
while True:
|
42
|
+
if not self.main_queue.empty():
|
43
|
+
command: dict = self.main_queue.get()
|
44
|
+
print(command)
|
45
|
+
|
46
|
+
c = command["type"]
|
47
|
+
if c in self.commandes.keys():
|
48
|
+
del command['type']
|
49
|
+
try:
|
50
|
+
result = self.commandes[c](**command)
|
51
|
+
self.process_queue.put({'status': 'success', 'message': result})
|
52
|
+
except MultibotError as e:
|
53
|
+
self.process_queue.put({'status': 'error', 'message': e})
|
54
|
+
|
55
|
+
def start_bot_to_process(self, bot_name: str) -> str:
|
56
|
+
"""
|
57
|
+
Lance un unique bot
|
58
|
+
"""
|
59
|
+
self.if_bot_no_exist(bot_name)
|
60
|
+
self.__bots[bot_name].start()
|
61
|
+
return f'{bot_name} bot started'
|
62
|
+
|
63
|
+
def stop_bot_to_process(self, bot_name: str) -> str:
|
64
|
+
"""
|
65
|
+
Stop un bot du processus
|
66
|
+
:param bot_name: Le nom du bot à stopper
|
67
|
+
"""
|
68
|
+
self.if_bot_no_exist(bot_name)
|
69
|
+
self.__bots[bot_name].stop()
|
70
|
+
return f'{bot_name} bot stopped'
|
71
|
+
|
72
|
+
def start_all_bot_to_process(self) -> list[str]:
|
73
|
+
"""
|
74
|
+
Start tous les bots du processus
|
75
|
+
"""
|
76
|
+
result = []
|
77
|
+
for bot in self.__bots.keys():
|
78
|
+
result.append(self.start_bot_to_process(bot))
|
79
|
+
return result
|
80
|
+
|
81
|
+
def stop_all_bot_to_process(self) -> list[str]:
|
82
|
+
"""
|
83
|
+
Stop tous les bots du processus
|
84
|
+
"""
|
85
|
+
result = []
|
86
|
+
for bot in self.__bots.keys():
|
87
|
+
result.append(self.stop_bot_to_process(bot))
|
88
|
+
|
89
|
+
return result
|
90
|
+
|
91
|
+
def add_bot_to_process(self, bot_name: str, token: str, intents: Intents) -> str:
|
92
|
+
"""
|
93
|
+
Ajoute un bot au processus
|
94
|
+
:param bot_name: Le nom du bot
|
95
|
+
:param token: Le token du bot
|
96
|
+
:raise: BotAlreadyExistError si le bot existe déjà
|
97
|
+
"""
|
98
|
+
if bot_name in self.__bots.keys():
|
99
|
+
raise BotAlreadyExistError(bot_name)
|
100
|
+
self.__bots[bot_name] = DiscordBot(token, intents)
|
101
|
+
return f'Bot {bot_name} added'
|
102
|
+
|
103
|
+
def add_pyFile_commands(self, bot_bot_name: str, file: str, setup_function: str, reload_command: bool):
|
104
|
+
"""
|
105
|
+
Ajoute et charge un fichier de commande bot et ses dépendances.
|
106
|
+
Les fichiers doivent avoir une fonction appelée « setup » ou un équivalent passé en paramètre.
|
107
|
+
|
108
|
+
def setup(bot: Bot) :
|
109
|
+
...
|
110
|
+
|
111
|
+
:param bot_bot_name : Le nom du bot à ajouter au fichier de commandes
|
112
|
+
:param file: Chemin relatif ou absolue du fichier de commande
|
113
|
+
:param setup_function : Nom de la fonction appelée par le processus pour donner l'instance de Bot.
|
114
|
+
:param reload_command : Recharge toutes les commandes dans le fichier et les dépendances. Défaut : True
|
115
|
+
"""
|
116
|
+
self.if_bot_no_exist(bot_bot_name)
|
117
|
+
setup_function = Str_(setup_function).str_
|
118
|
+
file = Str_(file).str_
|
119
|
+
self.__bots[bot_bot_name].add_pyFile_commands(file=file, setup_function=setup_function, reload_command=reload_command)
|
120
|
+
|
121
|
+
def modify_pyFile_commands(self, bot_bot_name: str, file: str, setup_function: str):
|
122
|
+
"""
|
123
|
+
Modifie un fichier de comandes et le recharge.
|
124
|
+
Ne recharge que le fichier et non les commandes du bot !
|
125
|
+
:param bot_bot_name: Le nom du bot
|
126
|
+
:param file: Le chemin d'accès relatif ou absolue du fichier
|
127
|
+
"""
|
128
|
+
self.if_bot_no_exist(bot_bot_name)
|
129
|
+
file = Str_(file).str_
|
130
|
+
self.__bots[bot_bot_name].modify_pyFile_commands(file=file, setup_function=setup_function)
|
131
|
+
|
132
|
+
|
133
|
+
def reload_all_commands(self, bot_name: str):
|
134
|
+
"""
|
135
|
+
Recharge toutes les commandes sur Discord
|
136
|
+
"""
|
137
|
+
self.if_bot_no_exist(bot_name)
|
138
|
+
self.__bots[bot_name].reload_commands()
|
139
|
+
return f'Bot {bot_name} commands reloaded'
|
140
|
+
|
141
|
+
def remove_bot_to_process(self, bot_name: str) -> str:
|
142
|
+
"""
|
143
|
+
Coupe et enlève un bot au processus
|
144
|
+
:param bot_name: Le nom du bot à retirer
|
145
|
+
:raise:
|
146
|
+
"""
|
147
|
+
self.if_bot_no_exist(bot_name)
|
148
|
+
try:
|
149
|
+
self.__bots[bot_name].stop()
|
150
|
+
except BotNotStartedError:
|
151
|
+
pass
|
152
|
+
self.__bots[bot_name].close_ascyncio_loop()
|
153
|
+
del self.__bots[bot_name]
|
154
|
+
return f'Bot {bot_name} removed'
|
155
|
+
|
156
|
+
def is_started(self, bot_name: str) -> bool:
|
157
|
+
"""
|
158
|
+
Regarde si la connexion au Websocket est effectué
|
159
|
+
:param bot_name: Le nom du bot à vérifier
|
160
|
+
"""
|
161
|
+
self.if_bot_no_exist(bot_name)
|
162
|
+
return self.__bots[bot_name].is_running
|
163
|
+
|
164
|
+
def is_ready(self, bot_name: str) -> bool:
|
165
|
+
"""
|
166
|
+
Regarde si le bot est ready
|
167
|
+
:param bot_name: Le nom du bot à vérifier
|
168
|
+
"""
|
169
|
+
self.if_bot_no_exist(bot_name)
|
170
|
+
return self.__bots[bot_name].is_ready
|
171
|
+
|
172
|
+
def is_ws_ratelimited(self, bot_name: str) -> bool:
|
173
|
+
"""
|
174
|
+
Regarde si le bot est ratelimit
|
175
|
+
:param bot_name: Le nom du bot à vérifier
|
176
|
+
"""
|
177
|
+
self.if_bot_no_exist(bot_name)
|
178
|
+
return self.__bots[bot_name].is_ws_ratelimited
|
179
|
+
|
180
|
+
def if_bot_no_exist(self, bot_name: str) -> None:
|
181
|
+
"""
|
182
|
+
Regarde si le bot existe dans la class
|
183
|
+
"""
|
184
|
+
if bot_name not in self.__bots.keys():
|
185
|
+
raise BotNotFoundError(bot_name)
|
186
|
+
|
187
|
+
def bot_count(self) -> int:
|
188
|
+
"""
|
189
|
+
Renvoie le nombre de bot au total
|
190
|
+
"""
|
191
|
+
return len(self.__bots)
|
192
|
+
|
193
|
+
def started_bot_count(self) -> int:
|
194
|
+
"""
|
195
|
+
Renvoie le nombre de bot démarré au total
|
196
|
+
"""
|
197
|
+
s = 0
|
198
|
+
for bot in self.__bots.values():
|
199
|
+
if bot.is_running:
|
200
|
+
s += 1
|
201
|
+
return s
|
202
|
+
|
203
|
+
def shutdown_bot_count(self) -> int:
|
204
|
+
"""
|
205
|
+
Renvoie le nombre de bot arrêter au total
|
206
|
+
"""
|
207
|
+
s = 0
|
208
|
+
for bot in self.__bots.values():
|
209
|
+
if not bot.is_running:
|
210
|
+
s += 1
|
211
|
+
return s
|
212
|
+
|
213
|
+
def get_bots_bot_name(self) -> list[str]:
|
214
|
+
"""
|
215
|
+
Renvoie tous les noms des bots entrée par l'utilisateur
|
216
|
+
"""
|
217
|
+
return list(self.__bots.keys())
|
@@ -23,6 +23,7 @@ class Pagination:
|
|
23
23
|
|
24
24
|
self.__view.add_items(Button(label='⏮', row=0, custom_id='back+', style=ButtonStyle.blurple))
|
25
25
|
self.__view.add_items(Button(label='◀', row=0, custom_id='back', style=ButtonStyle.blurple))
|
26
|
+
self.__view.add_items(Button(label='None', row=0, custom_id='counter', style=ButtonStyle.gray, disabled=True))
|
26
27
|
self.__view.add_items(Button(label='▶', row=0, custom_id='forward', style=ButtonStyle.blurple))
|
27
28
|
self.__view.add_items(Button(label='⏭', row=0, custom_id='forward+', style=ButtonStyle.blurple))
|
28
29
|
self.__view.set_callable('back+', 'back', 'forward', 'forward+', _callable=self.__turn_page)
|
@@ -38,6 +39,7 @@ class Pagination:
|
|
38
39
|
add_page(content="my message", embeds=[embed1, embed2], ...)
|
39
40
|
"""
|
40
41
|
self.__pages.append((args, kwargs))
|
42
|
+
self.__view.get_ui('counter').label = f"{self.__current_page+1}/{len(self.__pages)}"
|
41
43
|
return self
|
42
44
|
|
43
45
|
def delete_pages(self, *page_numbers: Union[str, int]) -> "Pagination":
|
@@ -54,6 +56,7 @@ class Pagination:
|
|
54
56
|
raise PageNumberNotFound(page_number)
|
55
57
|
|
56
58
|
del self.__pages[page_number]
|
59
|
+
self.__view.get_ui('counter').label = f"{self.__current_page+1}/{len(self.__pages)}"
|
57
60
|
return self
|
58
61
|
|
59
62
|
async def __turn_page(self, button, interaction: Interaction):
|
@@ -81,11 +84,14 @@ class Pagination:
|
|
81
84
|
elif interaction.custom_id == 'forward+': # Go to the last page
|
82
85
|
self.__current_page = page_count-1
|
83
86
|
|
87
|
+
self.__view.get_ui('counter').label = f"{self.__current_page + 1}/{len(self.__pages)}"
|
84
88
|
|
85
89
|
await interaction.message.edit(
|
86
90
|
|
87
91
|
*self.__pages[self.__current_page][0],
|
88
92
|
|
93
|
+
view=self.get_view,
|
94
|
+
|
89
95
|
**self.__pages[self.__current_page][1]
|
90
96
|
|
91
97
|
)
|
@@ -117,5 +123,6 @@ class Pagination:
|
|
117
123
|
def get_page(self) -> int:
|
118
124
|
"""
|
119
125
|
Get the number of showed page
|
126
|
+
(start to 0)
|
120
127
|
"""
|
121
128
|
return self.__current_page
|
@@ -1,10 +1,14 @@
|
|
1
|
+
from __future__ import annotations
|
1
2
|
from discord import Interaction, ApplicationContext, Message, Member, TextChannel
|
2
3
|
from discord.ui import View, Item
|
3
|
-
from typing import Union, Callable,
|
4
|
+
from typing import Union, Callable, TYPE_CHECKING
|
4
5
|
|
5
|
-
from ..typeViews import T_views
|
6
6
|
from .errors import CustomIDNotFound
|
7
7
|
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from ..menu.selectMenu import SelectMenu
|
10
|
+
from ..pagination.pagination_view import Pagination
|
11
|
+
|
8
12
|
|
9
13
|
class EasyModifiedViews(View):
|
10
14
|
"""
|
@@ -21,7 +25,7 @@ class EasyModifiedViews(View):
|
|
21
25
|
super().__init__(*items, timeout=timeout)
|
22
26
|
self.__timeout: Union[float, None] = timeout
|
23
27
|
self.__disabled_on_timeout: bool = disabled_on_timeout
|
24
|
-
self.__callback: dict[str, dict[str, Union[Callable[[Interaction], None],
|
28
|
+
self.__callback: dict[str, dict[str, Union[Callable[[Interaction], None], Item]]] = {}
|
25
29
|
self.__ctx: Union[Message, Interaction] = None
|
26
30
|
|
27
31
|
def __check_custom_id(self, custom_id: str) -> None:
|
@@ -46,19 +50,25 @@ class EasyModifiedViews(View):
|
|
46
50
|
self.__ctx = await target.send(*args, **kwargs)
|
47
51
|
|
48
52
|
def add_items(self,
|
49
|
-
*items:
|
53
|
+
*items: Union[Item, SelectMenu, Pagination]) -> "EasyModifiedViews":
|
50
54
|
"""
|
51
55
|
Add all items in the View.
|
52
56
|
"""
|
53
57
|
|
54
58
|
for ui in items:
|
55
59
|
|
56
|
-
|
57
|
-
|
60
|
+
if type(ui).__name__ in ('SelectMenu', 'Pagination'):
|
61
|
+
for item in ui.get_view.items:
|
62
|
+
self.add_items(item)
|
63
|
+
self.set_callable(item.custom_id, _callable=ui.get_callable(item.custom_id))
|
64
|
+
|
65
|
+
else:
|
66
|
+
self.__callback[ui.custom_id] = {'ui': ui, 'func': None}
|
67
|
+
self.add_item(ui)
|
58
68
|
|
59
69
|
return self
|
60
70
|
|
61
|
-
async def update_items(self, *items:
|
71
|
+
async def update_items(self, *items: Item) -> "EasyModifiedViews":
|
62
72
|
"""
|
63
73
|
Update all views.
|
64
74
|
Append items if custom_ids not in the view
|
@@ -67,7 +77,6 @@ class EasyModifiedViews(View):
|
|
67
77
|
"""
|
68
78
|
|
69
79
|
for item in items:
|
70
|
-
|
71
80
|
try:
|
72
81
|
self.__check_custom_id(item.custom_id)
|
73
82
|
self.__callback[item.custom_id]['ui'] = item
|
@@ -128,6 +137,14 @@ class EasyModifiedViews(View):
|
|
128
137
|
|
129
138
|
self.__callback[custom_id]['func'] = _callable
|
130
139
|
|
140
|
+
def get_callable(self, custom_id: str) -> Union[Callable, None]:
|
141
|
+
"""
|
142
|
+
Get the callable UI
|
143
|
+
:param custom_id: UI ID
|
144
|
+
"""
|
145
|
+
self.__check_custom_id(custom_id)
|
146
|
+
return self.__callback[custom_id]['func']
|
147
|
+
|
131
148
|
async def interaction_check(self, interaction: Interaction) -> bool:
|
132
149
|
"""
|
133
150
|
Func to apply items
|
@@ -243,25 +260,26 @@ class EasyModifiedViews(View):
|
|
243
260
|
"""
|
244
261
|
Update the View on the attached message.
|
245
262
|
"""
|
246
|
-
|
263
|
+
if self.is_finished():
|
264
|
+
return
|
247
265
|
|
248
|
-
|
249
|
-
|
266
|
+
if self.message:
|
267
|
+
await self.message.edit(view=self)
|
250
268
|
|
251
|
-
|
252
|
-
|
269
|
+
elif self.__ctx:
|
270
|
+
await self.__ctx.edit(view=self)
|
253
271
|
|
254
|
-
|
255
|
-
|
272
|
+
else:
|
273
|
+
return
|
256
274
|
|
257
275
|
@property
|
258
|
-
def get_uis(self) -> list[
|
276
|
+
def get_uis(self) -> list[Item]:
|
259
277
|
"""
|
260
278
|
Get all uis in the view
|
261
279
|
"""
|
262
280
|
return [i['ui'] for i in self.__callback.values()]
|
263
281
|
|
264
|
-
def get_ui(self, custom_id: str) ->
|
282
|
+
def get_ui(self, custom_id: str) -> Item:
|
265
283
|
"""
|
266
284
|
Get an ui in the view
|
267
285
|
:raise: CustomIDNotFound
|
@@ -270,4 +288,8 @@ class EasyModifiedViews(View):
|
|
270
288
|
return self.__callback[custom_id]['ui']
|
271
289
|
|
272
290
|
def __str__(self):
|
273
|
-
return str(self.__callback)
|
291
|
+
return str(self.__callback)
|
292
|
+
|
293
|
+
@property
|
294
|
+
def items(self) -> tuple[Item]:
|
295
|
+
return tuple([i['ui'] for i in self.__callback.values()])
|
@@ -0,0 +1,241 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: PycordViews
|
3
|
+
Version: 1.2.0
|
4
|
+
Summary: Views and multibot for py-cord library
|
5
|
+
Home-page: https://github.com/BOXERRMD/Py-cord_Views
|
6
|
+
Author: Chronos (alias BOXERRMD)
|
7
|
+
Author-email: vagabonwalybi@gmail.com
|
8
|
+
Maintainer: Chronos
|
9
|
+
License: MIT License
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
12
|
+
Classifier: Natural Language :: English
|
13
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 11
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
16
|
+
Classifier: Operating System :: MacOS
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
18
|
+
Requires-Python: >=3.9
|
19
|
+
Description-Content-Type: text/markdown
|
20
|
+
License-File: LICENSE
|
21
|
+
Requires-Dist: immutable-Python-type
|
22
|
+
Dynamic: author
|
23
|
+
Dynamic: author-email
|
24
|
+
Dynamic: classifier
|
25
|
+
Dynamic: description
|
26
|
+
Dynamic: description-content-type
|
27
|
+
Dynamic: home-page
|
28
|
+
Dynamic: license
|
29
|
+
Dynamic: license-file
|
30
|
+
Dynamic: maintainer
|
31
|
+
Dynamic: requires-dist
|
32
|
+
Dynamic: requires-python
|
33
|
+
Dynamic: summary
|
34
|
+
|
35
|
+
# Py-cord_Views
|
36
|
+
Views for py-cord library
|
37
|
+
|
38
|
+
# Paginator
|
39
|
+
The paginator instance is used to create a view acting as a “book”, with pages that can be turned using buttons.
|
40
|
+
## `Paginator`
|
41
|
+
> ```python
|
42
|
+
> Paginator(timeout: Union[float, None] = None, disabled_on_timeout: bool = False)`
|
43
|
+
> ```
|
44
|
+
> > **Method** `add_page(*args, **kwargs) -> Pagination` : add a new page with send message function parameters _(content, embed, embeds, files...)_
|
45
|
+
>
|
46
|
+
> > **Method** `delete_pages(*page_numbers: Union[str, int]) -> Pagination` : Deletes pages in the order in which they were added _(start to 0)_
|
47
|
+
>
|
48
|
+
> > **Method** `send(target: Union[Member, TextChannel]) -> Any` : Send the pagination in dm member or channels
|
49
|
+
>
|
50
|
+
> > **Method** `respond(ctx: ApplicationContext) -> Any` : Respond at slash command call
|
51
|
+
>
|
52
|
+
> > **@property** `get_view -> EasyModifiedViews` : Return the pagination view. Can be used in `view` parameter to setup a view
|
53
|
+
>
|
54
|
+
> > **@property** `get_page -> int` : get the showed page number
|
55
|
+
|
56
|
+
```python
|
57
|
+
from pycordViews.pagination import Pagination
|
58
|
+
import discord
|
59
|
+
|
60
|
+
intents = discord.Intents.all()
|
61
|
+
bot = discord.AutoShardedBot(intents=intents)
|
62
|
+
|
63
|
+
@bot.command(name="My command paginator", description="...")
|
64
|
+
async def pagination_command(ctx):
|
65
|
+
"""
|
66
|
+
Create a command pagination
|
67
|
+
"""
|
68
|
+
pages: Pagination = Pagination(timeout=None, disabled_on_timeout=False)
|
69
|
+
|
70
|
+
pages.add_page(content="It's my first page !!", embed=None)# reset embed else he show the embed of the page after
|
71
|
+
|
72
|
+
embed = discord.Embed(title="My embed title", description="..........")
|
73
|
+
pages.add_page(content=None, embed=embed) # reset content else he show the content of the page before
|
74
|
+
|
75
|
+
pages.add_page(content="My last page !", embed=None)# reset embed else he show the embed of the page before
|
76
|
+
|
77
|
+
await pages.respond(ctx=ctx) # respond to the command
|
78
|
+
await pages.send(send_to=ctx.author) # send the message to the command author
|
79
|
+
|
80
|
+
bot.run("Your token")
|
81
|
+
```
|
82
|
+
|
83
|
+
# SelectMenu
|
84
|
+
The SelectMenu instance is used to create drop-down menus that can be easily modified at will.
|
85
|
+
|
86
|
+
## `SelectMenu`
|
87
|
+
> ```python
|
88
|
+
> SelectMenu(timeout: Union[float, None] = None, disabled_on_timeout: bool = False)`
|
89
|
+
> ```
|
90
|
+
> > **Method** `add_string_select_menu(custom_id: str = None, placeholder: str = None, min_values: int = 1, max_values: int = 1, disabled=False, row=None) -> Menu` : Add a string select menu in the ui. Return Menu instance to set options
|
91
|
+
>
|
92
|
+
> > **Method** `add_user_select_menu(custom_id: str = None, placeholder: str = None, min_values: int = 1, max_values: int = 1, disabled=False, row=None) -> Menu` : Add a user select menu in the ui. Return Menu instance to set options
|
93
|
+
>
|
94
|
+
> > **Method** `add_role_select_menu(custom_id: str = None, placeholder: str = None, min_values: int = 1, max_values: int = 1, disabled=False, row=None) -> Menu` Add a role select menu in the ui. Return Menu instance to set options
|
95
|
+
>
|
96
|
+
> > **Method** `add_mentionnable_select_menu(custom_id: str = None, placeholder: str = None, min_values: int = 1, max_values: int = 1, disabled=False, row=None) -> Menu` : Add a mentionable select menu in the ui. Return Menu instance to set options
|
97
|
+
>
|
98
|
+
> > **Method** `set_callable(*custom_ids: str, _callable: Union[Callable, None]) -> SelectMenu` : Set a callable for menus associated with custom_ids. This callable _(async function)_ will be set to respond at selected menus interactions
|
99
|
+
>
|
100
|
+
> > **Method** `send(target: Union[Member, TextChannel]) -> Any` : Send the selectmenu in dm member or channels
|
101
|
+
>
|
102
|
+
> > **Method** `respond(ctx: ApplicationContext) -> Any` : Respond at slash command call
|
103
|
+
>
|
104
|
+
> > **Method** `update() -> None` : Update the view dynamically if there was sent before.
|
105
|
+
>
|
106
|
+
> > **@Method** `get_callable(self, custom_id: str) -> Union[Callable, None]` : Get the callable _async_ function link to the custom_id ui. If any callable set, return None
|
107
|
+
>
|
108
|
+
> > **@property** `get_view -> EasyModifiedViews` : Return the selectmenu view. Can be used in `view` parameter to setup a view
|
109
|
+
|
110
|
+
### Menu
|
111
|
+
|
112
|
+
## `Menu`
|
113
|
+
> ```python
|
114
|
+
> Menu(...)` # Not to be initialized by the user
|
115
|
+
> ```
|
116
|
+
> > **Method** `set_callable(_callable: Union[Callable, None]) -> Menu` : Set a callable for menus associated. This callable _(async function)_ will be set to respond at selected menus interactions
|
117
|
+
>
|
118
|
+
> > **Method** `add_option(label: str, value: str = MISSING, description: Union[str, None] = None, emoji: Union[str, Emoji, PartialEmoji, None] = None, default: bool = False) -> Menu` : Add a string select option. Only for string select menu !
|
119
|
+
>
|
120
|
+
> > **Method** `remove_options(*labels: str) -> Menu` : Remove options with labels. Only for string select menu !
|
121
|
+
>
|
122
|
+
> > **Method** `update_option(current_label: str, new_label: str = None, value: str = None, description: Union[str, None] = None, emoji: Union[str, Emoji, PartialEmoji, None] = None, default: Union[bool, None] = None) -> Menu` : Update option associated with `current_label` parameter
|
123
|
+
>
|
124
|
+
> > **@property** `component -> CustomSelect` : Return the Select component class
|
125
|
+
>
|
126
|
+
> > **@property** `selectmenu -> SelectMenu` : Return the current SelectMenu instance associated
|
127
|
+
>
|
128
|
+
> > **@property** `callable -> Callable` : Return the current callable menu
|
129
|
+
|
130
|
+
```python
|
131
|
+
from pycordViews.menu import SelectMenu
|
132
|
+
import discord
|
133
|
+
|
134
|
+
intents = discord.Intents.all()
|
135
|
+
bot = discord.AutoShardedBot(intents=intents)
|
136
|
+
|
137
|
+
@bot.command(name="My command select")
|
138
|
+
async def select_command(ctx):
|
139
|
+
"""
|
140
|
+
Create a command select
|
141
|
+
"""
|
142
|
+
async def your_response(select, interaction):
|
143
|
+
await interaction.response.send(f"You have selected {select.values[0]} !")
|
144
|
+
|
145
|
+
my_selector = SelectMenu(timeout=None, disabled_on_timeout=False) # A basic selector menu
|
146
|
+
my_menu = my_selector.add_string_select_menu(placeholder="Choice anything !") # add string_select UI
|
147
|
+
|
148
|
+
my_menu.add_option(label="My first choice !", emoji="😊", default=True, description="It's the first choice !", value='first choice')
|
149
|
+
my_menu.add_option(label="My second choice !", value='second choice')
|
150
|
+
my_menu.set_callable(your_response)
|
151
|
+
|
152
|
+
await my_selector.respond(ctx)
|
153
|
+
|
154
|
+
bot.run("Your token")
|
155
|
+
```
|
156
|
+
|
157
|
+
# Multibot
|
158
|
+
The Multibot instance is used to manage several bots dynamically.
|
159
|
+
|
160
|
+
Each instance of this class creates a process where bots can be added. These bots will each be launched in a different thread with their own asyncio loop.
|
161
|
+
|
162
|
+
## `Multibot`
|
163
|
+
> ```python
|
164
|
+
> Multibot(global_timeout: int = 30) # global_timeout is the time in seconds the program waits before abandoning the request
|
165
|
+
> ```
|
166
|
+
> > **Method** `add_bot(bot_name: str, token: str, intents: Intents) -> None` : Add a bot. The name given here is not the real bot name, it's juste an ID
|
167
|
+
>
|
168
|
+
> > **Method** `remove_bot(bot_name: str) -> dict[str, str]` : Remove à bot. If the bot is online, it will turn off properly. It can take a long time !
|
169
|
+
>
|
170
|
+
> > **Method** `start(*bot_names: str) -> list[dict[str, str]]` : Start bots
|
171
|
+
>
|
172
|
+
> > **Method** `def stop(*bot_names: str) -> list[dict[str, str]]` : Stop bots properly
|
173
|
+
>
|
174
|
+
> > **Method** `start_all() -> list[dict[str, list[str]]]` : Start all bot in the process
|
175
|
+
>
|
176
|
+
> > **Method** `stop_all() -> list[dict[str, list[str]]]` : Stop all bot in the process properly
|
177
|
+
>
|
178
|
+
> > **Method** `is_started(bot_name: str) -> bool` : Return if the bot is connected at the Discord WebSocket
|
179
|
+
>
|
180
|
+
> > **Method** `is_ready(bot_name: str) -> bool` : Return if the bot is ready in Discord
|
181
|
+
>
|
182
|
+
> > **Method** `is_ws_ratelimited(bot_name: str) -> bool` : Return if the bot is rate limited by Discord
|
183
|
+
>
|
184
|
+
> > **Method** `reload_commands(*bot_names: str) -> list[dict[str, str]]` : Reload all commands for each bot passed in parameters
|
185
|
+
>
|
186
|
+
> > **Method** `add_pyFile_commands(bot_name: str, file: str, setup_function: str = 'setup', reload_command: bool = True) -> dict[str, str]` : Add a python Discord command file to the bot. `file` parameter require a file path, absolute or not. By default, it automatically reloads commands on the bot after adding the file.
|
187
|
+
> >
|
188
|
+
> > ### _Blob_commands.py_
|
189
|
+
> > ```python
|
190
|
+
> > """
|
191
|
+
> > Discord command file example. Follow it !
|
192
|
+
> > This file doesn't have 'bot.run()' function. It's managed by Multibot instance.
|
193
|
+
> > """
|
194
|
+
> >
|
195
|
+
> > from discord import Bot, ApplicationContext, Message # autoaticly imported by importlib module
|
196
|
+
> >
|
197
|
+
> > # this function is called when the bot load a python file command. It is mandatory to have it with a bot parameter !
|
198
|
+
> > def setup(bot: Bot):
|
199
|
+
> > """
|
200
|
+
> > Function called by the process and give the bot instance.
|
201
|
+
> > This function is mandatory to have it with a bot parameter but can be renamed with 'setup_function' parameter in 'add_pyFile_commands' method.
|
202
|
+
> > Every discord command and discord event can be in this function, but you can make function and class outside setup function.
|
203
|
+
> > -> bot : Instance of your started bot.
|
204
|
+
> > """
|
205
|
+
> > @bot.command()
|
206
|
+
> > async def my_first_command(ctx: ApplicationContext):
|
207
|
+
> > await ctx.respond("It's my first command !")
|
208
|
+
> >
|
209
|
+
> > @bot.event
|
210
|
+
> > async def on_message(message: Message):
|
211
|
+
> > await message.add_reaction(emoji="😊")
|
212
|
+
> >
|
213
|
+
> > # You don't need 'bot.run(...)' here !
|
214
|
+
> > ```
|
215
|
+
>
|
216
|
+
> > **Method** `modify_pyFile_commands(bot_name: str, file: str, setup_function: str = 'setup') -> dict[str, str]` : Modify python discord command file and setup function. This method doesn't reload automatically commands on the bot. Use `reload_commands` after. `file` parameter require a file path, absolute or not.
|
217
|
+
>
|
218
|
+
> > **@property** `bot_count -> int` : Return the total number of bots
|
219
|
+
>
|
220
|
+
> > **@property** `started_bot_count -> int` : Return the total number of started bots
|
221
|
+
>
|
222
|
+
> > **@property** `shutdown_bot_count( -> int` : Return the total number of shutdown bots
|
223
|
+
>
|
224
|
+
> > **@property** `get_bots_name -> list[str]` : Return all bots name _(not real name of bots)_
|
225
|
+
```python
|
226
|
+
from pycordViews.multibot import Multibot
|
227
|
+
from discord import Intents
|
228
|
+
|
229
|
+
if __name__ == '__main__': # Obligatory !
|
230
|
+
process = Multibot()
|
231
|
+
|
232
|
+
process.add_bot(bot_name="Blob", token="TOKEN FIRST BOT", intents=Intents.all())
|
233
|
+
process.add_bot(bot_name="Bee", token="TOKEN SECOND BOT", intents=Intents.all())
|
234
|
+
|
235
|
+
process.start_all()
|
236
|
+
process.add_pyFile_commands(bot_name='Blob', file='Blob_commands.py', reload_command=True)
|
237
|
+
process.add_pyFile_commands(bot_name='Bee', file='Bee_commandes.py', reload_command=True)
|
238
|
+
|
239
|
+
process.modify_pyFile_commands(bot_name='Blob', file='others_commands/Blob2_commands.py', setup_function='started_bot')
|
240
|
+
process.reload_commands('Blob')
|
241
|
+
```
|
@@ -0,0 +1,21 @@
|
|
1
|
+
pycordViews/__init__.py,sha256=gJmACNCnfY5Q00aUFVODZ6Tkfdo3mgGlolPNSZnzT8k,225
|
2
|
+
pycordViews/menu/__init__.py,sha256=QUXA9ezyeTScvS1kxMNFgKEbZMsPq5yUnWWgbXCytCk,97
|
3
|
+
pycordViews/menu/errors.py,sha256=0Um-oH5qMdWSZB_bGlqILsf9WSDtC4n_HwkheekiMV4,480
|
4
|
+
pycordViews/menu/menu.py,sha256=3D13C5Vs69w9SlrnixBfF0riY48jORi8JcYte65YFPs,4306
|
5
|
+
pycordViews/menu/selectMenu.py,sha256=oYydLtoQm7YRjnFII2af5cUfIZ8ci_86lk0Il5TGYTw,10459
|
6
|
+
pycordViews/multibot/__init__.py,sha256=93Q_URiRUMsvwQJIqUnb75aq6SPM83yteSMrH0rmXMg,30
|
7
|
+
pycordViews/multibot/bot.py,sha256=UrRqSSmtFpkQKAqeunHxIy_O5DgO1gcW-nMEQ9yNdAo,8267
|
8
|
+
pycordViews/multibot/errors.py,sha256=_xawL8jQZTajx253F4JKywFGVPdYf8vTALOMiNqJ9hs,1176
|
9
|
+
pycordViews/multibot/multibot.py,sha256=xko7AdgkuYJgefKl39Rmz9fpk8R02nGdSWf6zCt_CxQ,7562
|
10
|
+
pycordViews/multibot/process.py,sha256=MYhOUG1egKK_I8sJ8y4yW_avZut8VIyGO5D2d-CCBV4,7895
|
11
|
+
pycordViews/pagination/__init__.py,sha256=Z9BcdoTWyC1KXGwQ1_C0tu9rkZpdrrjEHwMmjXsckeE,81
|
12
|
+
pycordViews/pagination/errors.py,sha256=CYb5gBcXx0kYDUDkNpfUrqSxQAcJE_qfpomWtUFOsTk,316
|
13
|
+
pycordViews/pagination/pagination_view.py,sha256=sJHDkmiTGwJzxfAUQijF2ry6NPVwepJvbdkAvA6j0es,4846
|
14
|
+
pycordViews/views/__init__.py,sha256=yligptZmw-np8tjKLr76SVmi0807Nk6jCyKkKYLhbCY,89
|
15
|
+
pycordViews/views/easy_modified_view.py,sha256=QuS8AnkTLHrdKl7Tya0_gjuwhWHLIvMAEK3DsyE1ALg,8986
|
16
|
+
pycordViews/views/errors.py,sha256=0NBjBDaSFZChJua1ho9qyfbNzwXy1U6Kcob5CCUxBK8,218
|
17
|
+
pycordviews-1.2.0.dist-info/licenses/LICENSE,sha256=lNgcw1_xb7QENAQi3uHGymaFtbs0RV-ihiCd7AoLQjA,1082
|
18
|
+
pycordviews-1.2.0.dist-info/METADATA,sha256=ij56btgSo1NEh_GL3G4CDiPO3Rhr6exSE0OeeERlNCY,11828
|
19
|
+
pycordviews-1.2.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
20
|
+
pycordviews-1.2.0.dist-info/top_level.txt,sha256=nqBU40KmnSCjtry8kmv97-RvZC-8xQrhrrloOJX2ROs,91
|
21
|
+
pycordviews-1.2.0.dist-info/RECORD,,
|