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.
@@ -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, Iterable
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], T_views]]] = {}
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: T_views) -> "EasyModifiedViews":
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
- self.__callback[ui.custom_id] = {'ui': ui, 'func': None}
57
- self.add_item(ui)
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: T_views) -> "EasyModifiedViews":
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
- try:
263
+ if self.is_finished():
264
+ return
247
265
 
248
- if self.message:
249
- await self.message.edit(view=self)
266
+ if self.message:
267
+ await self.message.edit(view=self)
250
268
 
251
- else:
252
- await self.__ctx.edit(view=self)
269
+ elif self.__ctx:
270
+ await self.__ctx.edit(view=self)
253
271
 
254
- except:
255
- pass
272
+ else:
273
+ return
256
274
 
257
275
  @property
258
- def get_uis(self) -> list[T_views]:
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) -> T_views:
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5