PycordViews 1.2.4__tar.gz → 1.2.6__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 (32) hide show
  1. {pycordviews-1.2.4 → pycordviews-1.2.6}/PKG-INFO +1 -1
  2. {pycordviews-1.2.4 → pycordviews-1.2.6}/PycordViews.egg-info/PKG-INFO +1 -1
  3. {pycordviews-1.2.4 → pycordviews-1.2.6}/PycordViews.egg-info/SOURCES.txt +8 -0
  4. {pycordviews-1.2.4 → pycordviews-1.2.6}/PycordViews.egg-info/top_level.txt +1 -0
  5. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/__init__.py +1 -0
  6. pycordviews-1.2.6/pycordViews/kit/__init__.py +2 -0
  7. pycordviews-1.2.6/pycordViews/kit/confirm.py +69 -0
  8. pycordviews-1.2.6/pycordViews/kit/poll.py +147 -0
  9. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/pagination/__init__.py +1 -0
  10. pycordviews-1.2.6/pycordViews/pagination/page.py +25 -0
  11. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/pagination/pagination_view.py +24 -16
  12. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/views/easy_modified_view.py +62 -22
  13. pycordviews-1.2.6/pycordViews/views/errors.py +13 -0
  14. {pycordviews-1.2.4 → pycordviews-1.2.6}/setup.py +2 -2
  15. pycordviews-1.2.4/pycordViews/views/errors.py +0 -9
  16. {pycordviews-1.2.4 → pycordviews-1.2.6}/LICENSE +0 -0
  17. {pycordviews-1.2.4 → pycordviews-1.2.6}/PycordViews.egg-info/dependency_links.txt +0 -0
  18. {pycordviews-1.2.4 → pycordviews-1.2.6}/PycordViews.egg-info/requires.txt +0 -0
  19. {pycordviews-1.2.4 → pycordviews-1.2.6}/README.md +0 -0
  20. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/menu/__init__.py +0 -0
  21. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/menu/errors.py +0 -0
  22. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/menu/menu.py +0 -0
  23. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/menu/selectMenu.py +0 -0
  24. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/multibot/__init__.py +0 -0
  25. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/multibot/bot.py +0 -0
  26. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/multibot/errors.py +0 -0
  27. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/multibot/multibot.py +0 -0
  28. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/multibot/process.py +0 -0
  29. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/pagination/errors.py +0 -0
  30. {pycordviews-1.2.4 → pycordviews-1.2.6}/pycordViews/views/__init__.py +0 -0
  31. {pycordviews-1.2.4 → pycordviews-1.2.6}/pyproject.toml +0 -0
  32. {pycordviews-1.2.4 → pycordviews-1.2.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PycordViews
3
- Version: 1.2.4
3
+ Version: 1.2.6
4
4
  Summary: Views and multibot for py-cord library
5
5
  Home-page: https://github.com/BOXERRMD/Py-cord_Views
6
6
  Author: Chronos (alias BOXERRMD)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PycordViews
3
- Version: 1.2.4
3
+ Version: 1.2.6
4
4
  Summary: Views and multibot for py-cord library
5
5
  Home-page: https://github.com/BOXERRMD/Py-cord_Views
6
6
  Author: Chronos (alias BOXERRMD)
@@ -8,6 +8,9 @@ PycordViews.egg-info/dependency_links.txt
8
8
  PycordViews.egg-info/requires.txt
9
9
  PycordViews.egg-info/top_level.txt
10
10
  pycordViews/__init__.py
11
+ pycordViews/kit/__init__.py
12
+ pycordViews/kit/confirm.py
13
+ pycordViews/kit/poll.py
11
14
  pycordViews/menu/__init__.py
12
15
  pycordViews/menu/errors.py
13
16
  pycordViews/menu/menu.py
@@ -19,10 +22,14 @@ pycordViews/multibot/multibot.py
19
22
  pycordViews/multibot/process.py
20
23
  pycordViews/pagination/__init__.py
21
24
  pycordViews/pagination/errors.py
25
+ pycordViews/pagination/page.py
22
26
  pycordViews/pagination/pagination_view.py
23
27
  pycordViews/views/__init__.py
24
28
  pycordViews/views/easy_modified_view.py
25
29
  pycordViews/views/errors.py
30
+ pycordViews/kit/__init__.py
31
+ pycordViews/kit/confirm.py
32
+ pycordViews/kit/poll.py
26
33
  pycordViews/menu/__init__.py
27
34
  pycordViews/menu/errors.py
28
35
  pycordViews/menu/menu.py
@@ -34,6 +41,7 @@ pycordViews/multibot/multibot.py
34
41
  pycordViews/multibot/process.py
35
42
  pycordViews/pagination/__init__.py
36
43
  pycordViews/pagination/errors.py
44
+ pycordViews/pagination/page.py
37
45
  pycordViews/pagination/pagination_view.py
38
46
  pycordViews/views/__init__.py
39
47
  pycordViews/views/easy_modified_view.py
@@ -1,4 +1,5 @@
1
1
  pycordViews
2
+ pycordViews/kit
2
3
  pycordViews/menu
3
4
  pycordViews/multibot
4
5
  pycordViews/pagination
@@ -3,3 +3,4 @@ from .pagination.pagination_view import Pagination
3
3
  from .multibot import Multibot
4
4
  from .menu.selectMenu import SelectMenu
5
5
  from .menu.menu import Menu, CustomSelect
6
+ from .kit import *
@@ -0,0 +1,2 @@
1
+ from .poll import Poll
2
+ from .confirm import Confirm
@@ -0,0 +1,69 @@
1
+ from typing import Optional
2
+ from ..views.easy_modified_view import EasyModifiedViews
3
+ from discord import Interaction, ButtonStyle, Bot
4
+ from discord.ui import Button
5
+ from asyncio import wait_for, Future, get_event_loop, TimeoutError
6
+
7
+
8
+ class Confirm:
9
+
10
+ def __init__(self, timeout: Optional[float] = None, disable_on_click: bool = False):
11
+ """
12
+ Init a Confirm class instance kit.
13
+ :param timeout: Time before end the view
14
+ """
15
+ self.__timeout: float = timeout
16
+ self.__disable_on_click: bool = disable_on_click
17
+ self.__view: EasyModifiedViews = EasyModifiedViews(disabled_on_timeout=True, timeout=timeout, call_on_timeout=self._on_timeout)
18
+ self.__button_confirm: Button = Button(label='Confirm', emoji='✅', style=ButtonStyle.green, custom_id='Confirm_confirm')
19
+ self.__button_denied: Button = Button(label='Denied', emoji='❌', style=ButtonStyle.gray, custom_id='Confirm_denied')
20
+ self.__view.add_items(self.__button_confirm, self.__button_denied)
21
+ self.__view.set_callable(self.__button_confirm.custom_id, _callable=self._confirm)
22
+ self.__view.set_callable(self.__button_denied.custom_id, _callable=self._denied)
23
+
24
+ self.__future: Future = get_event_loop().create_future()
25
+
26
+ async def _confirm(self, button: Button, interaction: Interaction):
27
+ """
28
+ Base asynchronous _confirm function called when button is pressed
29
+ """
30
+ if not self.__future.done():
31
+ self.__future.set_result(True)
32
+ if self.__disable_on_click:
33
+ await self.__view.disable_items('Confirm_confirm', 'Confirm_denied')
34
+ await interaction.response.defer()
35
+
36
+ async def _denied(self, button: Button, interaction: Interaction):
37
+ """
38
+ Base asynchronous _denied function called when button is pressed
39
+ """
40
+ if not self.__future.done():
41
+ self.__future.set_result(False)
42
+ if self.__disable_on_click:
43
+ await self.__view.disable_items('Confirm_confirm', 'Confirm_denied')
44
+ await interaction.response.defer()
45
+
46
+ async def wait_for_response(self) -> Optional[bool]:
47
+ """
48
+ Wait and return the result of current button pressed.
49
+ :return: True if it is confirmed, False else. If timeout is reached, return None
50
+ """
51
+ try:
52
+ return await wait_for(self.__future, timeout=self.__timeout)
53
+
54
+ except TimeoutError:
55
+ return None
56
+
57
+ async def _on_timeout(self, ctx):
58
+ """
59
+ Called when the timeout is reached to end the Future
60
+ """
61
+ if not self.__future.done():
62
+ self.__future.set_result(None)
63
+
64
+ @property
65
+ def get_view(self) -> EasyModifiedViews:
66
+ """
67
+ Get the current view
68
+ """
69
+ return self.__view
@@ -0,0 +1,147 @@
1
+ from ..views.easy_modified_view import EasyModifiedViews
2
+ from ..views.errors import CoroutineError
3
+ from discord.ui.button import Button, ButtonStyle
4
+ from discord import Emoji, PartialEmoji, Embed, Color
5
+ from typing import Optional, Callable, Union
6
+ from asyncio import iscoroutinefunction
7
+
8
+
9
+ class Poll:
10
+
11
+ def __init__(self, timeout: Optional[float] = None, unique_vote: bool = True):
12
+ """
13
+ Class instance to make poll with buttons
14
+ :param timeout: Time before end the view
15
+ :param unique_vote: Set True to make vote unchangeable
16
+ """
17
+ self.yes_count: int = 0
18
+ self.no_count: int = 0
19
+ self.__unique_vote: bool = unique_vote
20
+ self.__view = EasyModifiedViews(disabled_on_timeout=True, timeout=timeout)
21
+ self.__button_yes = Button(label='Yes', emoji='✅', custom_id='Poll_yes', style=ButtonStyle.green)
22
+ self.__button_no = Button(label='No', emoji='❌', custom_id='Poll_no', style=ButtonStyle.gray)
23
+ self.__view.add_items(self.__button_yes, self.__button_no)
24
+ self.__view.set_callable('Poll_yes', _callable=self.yes)
25
+ self.__view.set_callable('Poll_no', _callable=self.no)
26
+ self.__view.call_on_timeout(self._result) # asynchrone fonction quand la view est arrivé à son timeout
27
+ self.__clicked_members: list[int] = []
28
+
29
+ def add_answer_button(self, label: str,
30
+ custom_id: str,
31
+ emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,
32
+ style: ButtonStyle = ButtonStyle.secondary,
33
+ row: Optional[int] = None,
34
+ _callable: Optional[Callable] = None) -> Button:
35
+ """
36
+ Create a new answer button and add it in the view.
37
+ :param label: Button label
38
+ :param custom_id: Button ID
39
+ :param emoji: Button emoji
40
+ :param style: Button style
41
+ :param row: Button row
42
+ :param _callable: Asynchronous function linked to the button interaction. It's called when the button is pressed
43
+ """
44
+
45
+ if not iscoroutinefunction(_callable):
46
+ raise CoroutineError(_callable)
47
+
48
+ b = Button(label=label, emoji=emoji, style=style, row=row, custom_id=custom_id)
49
+ self.__view.add_items(b)
50
+ self.__view.set_callable(custom_id, _callable=_callable)
51
+
52
+ return b
53
+
54
+
55
+ def set_yes_button(self, label: Optional[str] = None,
56
+ emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,
57
+ style: Optional[ButtonStyle] = None,
58
+ row: Optional[int] = None,
59
+ _callable: Optional[Callable] = None):
60
+ """
61
+ Set yes button parameters (he didn't change dynamically if the view was sent before)
62
+ :param label: Button label
63
+ :param emoji: Button emoji
64
+ :param style: Button style
65
+ :param row: Button row
66
+ :param _callable: Asynchronous function linked to the button interaction. It's called when the button is pressed
67
+ """
68
+ self.__button_yes.emoji = emoji if emoji is not None else self.__button_yes.emoji
69
+ self.__button_yes.style = style if style is not None else self.__button_yes.style
70
+ self.__button_yes.label = label if label is not None else self.__button_yes.label
71
+ self.__button_yes.row = row if row is not None else self.__button_yes.row
72
+
73
+ if not iscoroutinefunction(_callable):
74
+ raise CoroutineError(_callable)
75
+
76
+ if _callable is not None:
77
+ self.__view.set_callable(self.__button_yes.custom_id, _callable=_callable)
78
+
79
+ def set_no_button(self, label: Optional[str] = None,
80
+ emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,
81
+ style: Optional[ButtonStyle] = None,
82
+ row: Optional[int] = None,
83
+ _callable: Optional[Callable] = None):
84
+ """
85
+ Set no button parameters (he didn't change dynamically if the view was sent before)
86
+ :param label: Button label
87
+ :param emoji: Button emoji
88
+ :param row: Button row
89
+ :param style: Button style
90
+ :param callable: Asynchronous function linked to the button interaction. It's called when the button is pressed
91
+ """
92
+ self.__button_no.emoji = emoji if emoji is not None else self.__button_no.emoji
93
+ self.__button_no.style = style if style is not None else self.__button_no.style
94
+ self.__button_no.label = label if label is not None else self.__button_no.label
95
+ self.__button_no.row = row if row is not None else self.__button_no.row
96
+
97
+ if not iscoroutinefunction(_callable):
98
+ raise CoroutineError(_callable)
99
+
100
+ if _callable is not None:
101
+ self.__view.set_callable(self.__button_no.custom_id, _callable=_callable)
102
+
103
+ async def yes(self, button, interaction):
104
+ """
105
+ Base asynchronous function when "yes" button is pressed.
106
+ Increment “yes_count” attribute when pressed and respond with an ephemeral message.
107
+ This function can be changed.
108
+ """
109
+ if self.__unique_vote and interaction.user.id in self.__clicked_members:
110
+ await interaction.response.send_message(f"You have already voted !", ephemeral=True)
111
+ return
112
+ self.yes_count += 1
113
+ self.__clicked_members.append(interaction.user.id)
114
+ await interaction.response.send_message(f"You have selected : {button.label}", ephemeral=True)
115
+
116
+ async def no(self, button, interaction):
117
+ """
118
+ Base asynchronous function when "no" button is pressed.
119
+ Increment “yes_count” attribute when pressed and respond with an ephemeral message.
120
+ This function can be changed.
121
+ """
122
+ if self.__unique_vote and interaction.user.id in self.__clicked_members:
123
+ await interaction.response.send_message(f"You have already voted !", ephemeral=True)
124
+ return
125
+ self.yes_count += 1
126
+ self.__clicked_members.append(interaction.user.id)
127
+ await interaction.response.send_message(f"You have selected : {button.label}", ephemeral=True)
128
+
129
+ async def _result(self, ctx):
130
+ """
131
+ Asynchronous base function called when timeout is reached.
132
+ :param ctx: Actual command context
133
+ """
134
+ embed = Embed(title="Poll ended !",
135
+ description=f"## Results :\n"
136
+ f" `-` {self.__button_yes.emoji.name} {self.__button_yes.label} -> `{self.yes_count}`\n"
137
+ f" `-` {self.__button_no.emoji.name} {self.__button_no.label} -> `{self.no_count}`",
138
+ color=Color.gold())
139
+
140
+ await self.__view.message.reply(embed=embed)
141
+
142
+ @property
143
+ def get_view(self) -> EasyModifiedViews:
144
+ """
145
+ Get the current view
146
+ """
147
+ return self.__view
@@ -1,2 +1,3 @@
1
1
  from .pagination_view import Pagination
2
2
  from .errors import PageNumberNotFound
3
+ from .page import Page
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+ from typing import Optional, Callable
3
+ from discord import File, Embed
4
+ from immutableType import callable_, NoneType
5
+ from ..views import EasyModifiedViews
6
+
7
+ class Page:
8
+
9
+ @callable_(is_class=True, kwargs_types={'view': [NoneType, EasyModifiedViews], 'content': [NoneType, str], 'embed': [NoneType, Embed], 'embeds': [list], 'file': [NoneType, File], 'files': [list]})
10
+ def __init__(self, view: EasyModifiedViews, content: Optional[str] = None, embed: Optional[Embed] = None, embeds: list[Embed] = [], file: Optional[File] = None, files: Optional[list[File]] = []):
11
+ """
12
+ Init Page instance from Pagination class
13
+ """
14
+ self.__view: EasyModifiedViews = view if view is not None else EasyModifiedViews()
15
+ self.content: Optional[str] = content
16
+ self.embeds: list[Embed] = embeds if embed is None else embeds + [embed]
17
+ self.files: list[File] = files if file is None else files + [file]
18
+
19
+ @property
20
+ def get_page_view(self) -> EasyModifiedViews:
21
+ """
22
+ Get the current page view
23
+ """
24
+ return self.__view
25
+
@@ -1,9 +1,10 @@
1
1
  from ..views.easy_modified_view import EasyModifiedViews
2
- from discord import ButtonStyle, Interaction, TextChannel, Member, ApplicationContext
2
+ from discord import ButtonStyle, Interaction, TextChannel, Member, ApplicationContext, File, Embed
3
3
  from discord.ui import Button
4
4
  from .errors import PageNumberNotFound
5
+ from .page import Page
5
6
 
6
- from typing import Union, Any
7
+ from typing import Union, Any, Optional
7
8
 
8
9
 
9
10
  class Pagination:
@@ -28,17 +29,17 @@ class Pagination:
28
29
  self.__view.add_items(Button(label='⏭', row=0, custom_id='forward+', style=ButtonStyle.blurple))
29
30
  self.__view.set_callable('back+', 'back', 'forward', 'forward+', _callable=self.__turn_page)
30
31
 
31
- self.__pages: list[tuple[tuple, dict]] = []
32
+ self.__pages: list[Page] = []
32
33
  self.__current_page: int = 0
33
34
 
34
- def add_page(self, *args, **kwargs) -> "Pagination":
35
+ def add_page(self, content: Optional[str] = None, embed: Optional[Embed] = None, embeds: list[Embed] = [], file: Optional[File] = None, files: Optional[list[File]] = [], view: Optional[EasyModifiedViews] = None) -> "Pagination":
35
36
  """
36
37
  Adds a page (in a list) as if this function directly sent the message
37
38
  Pages are just modified and not reset ! Don't forget to disable embeds or content if the page don't need this.
38
39
 
39
40
  add_page(content="my message", embeds=[embed1, embed2], ...)
40
41
  """
41
- self.__pages.append((args, kwargs))
42
+ self.__pages.append(Page(content=content, embed=embed, embeds=embeds, file=file, files=files, view=view))
42
43
  self.__view.get_ui('counter').label = f"{self.__current_page+1}/{len(self.__pages)}"
43
44
  return self
44
45
 
@@ -86,13 +87,15 @@ class Pagination:
86
87
 
87
88
  self.__view.get_ui('counter').label = f"{self.__current_page + 1}/{len(self.__pages)}"
88
89
 
89
- await interaction.message.edit(
90
+ c = self.__pages[self.__current_page]
90
91
 
91
- *self.__pages[self.__current_page][0],
92
+ await interaction.message.edit(
92
93
 
93
- view=self.get_view,
94
+ content=c.content,
95
+ embeds=c.embeds,
96
+ files=c.files,
94
97
 
95
- **self.__pages[self.__current_page][1]
98
+ view=self.__view.copy() + self.__pages[self.__current_page].get_page_view,
96
99
 
97
100
  )
98
101
 
@@ -104,23 +107,28 @@ class Pagination:
104
107
  Send pagination without introduction message.
105
108
  :param target: The member or channel to send the pagination
106
109
  """
107
- return await self.__view.send(ctx=target, *self.__pages[0][0], **self.__pages[0][1], view=self.get_view)
110
+ first_page = self.__pages[0]
111
+ return await self.__view.send(target=target, content=first_page.content, embeds=first_page.embeds, files=first_page.files, view=self.__view.copy() + first_page.get_page_view)
108
112
 
109
113
  async def respond(self, ctx: ApplicationContext) -> Any:
110
114
  """
111
115
  Respond to the command call
116
+ :param ctx: ApplicationContext to respond
112
117
  """
113
- return await self.__view.respond(ctx=ctx, *self.__pages[0][0], **self.__pages[0][1], view=self.get_view)
118
+ first_page = self.__pages[0]
119
+ return await self.__view.respond(ctx=ctx, content=first_page.content, embeds=first_page.embeds, files=first_page.files, view=self.__view.copy() + first_page.get_page_view)
114
120
 
115
- @property
116
- def get_view(self) -> EasyModifiedViews:
121
+ def get_page(self, page_number: int) -> Page:
117
122
  """
118
- Get the view of pagination
123
+ Get the page
119
124
  """
120
- return self.__view
125
+ if 0 <= page_number <= len(self.__pages):
126
+ return self.__pages[page_number]
127
+ else:
128
+ raise PageNumberNotFound(page_number)
121
129
 
122
130
  @property
123
- def get_page(self) -> int:
131
+ def get_current_showed_page(self) -> int:
124
132
  """
125
133
  Get the number of showed page
126
134
  (start to 0)
@@ -1,13 +1,16 @@
1
1
  from __future__ import annotations
2
- from discord import Interaction, ApplicationContext, Message, Member, TextChannel
2
+ from discord import Interaction, ApplicationContext, Message, Member
3
+ from discord.abc import GuildChannel
3
4
  from discord.ui import View, Item
4
- from typing import Union, Callable, TYPE_CHECKING
5
+ from typing import Union, Callable, TYPE_CHECKING, Optional
6
+ from asyncio import iscoroutinefunction, create_task
5
7
 
6
- from .errors import CustomIDNotFound
8
+ from .errors import CustomIDNotFound, CoroutineError
7
9
 
8
10
  if TYPE_CHECKING:
9
11
  from ..menu.selectMenu import SelectMenu
10
12
  from ..pagination.pagination_view import Pagination
13
+ from ..kit import Poll, Confirm
11
14
 
12
15
 
13
16
  class EasyModifiedViews(View):
@@ -17,16 +20,19 @@ class EasyModifiedViews(View):
17
20
  Allows you to easily modify and replace an ui.
18
21
  """
19
22
 
20
- def __init__(self, timeout: Union[float, None] = None, disabled_on_timeout: bool = False, *items: Item):
23
+ def __init__(self, timeout: Optional[float] = None, disabled_on_timeout: bool = False, call_on_timeout: Optional[Callable] = None, *items: Item):
21
24
  """
22
25
  Init a Class view for Discord UI
23
26
  :param timeout: The time before ui disable
27
+ :param disabled_on_timeout: disable view if timeout is reached
28
+ :param call_on_timeout: asynchronous function to call after view timed-out
24
29
  """
25
30
  super().__init__(*items, timeout=timeout)
26
- self.__timeout: Union[float, None] = timeout
31
+ self.__timeout: Optional[float] = timeout
27
32
  self.__disabled_on_timeout: bool = disabled_on_timeout
28
33
  self.__callback: dict[str, dict[str, Union[Callable[[Interaction], None], Item]]] = {}
29
34
  self.__ctx: Union[Message, Interaction] = None
35
+ self.__call_on_timeout: Callable = call_on_timeout
30
36
 
31
37
  def __check_custom_id(self, custom_id: str) -> None:
32
38
  """
@@ -43,21 +49,24 @@ class EasyModifiedViews(View):
43
49
  """
44
50
  self.__ctx = await ctx.respond(*args, **kwargs)
45
51
 
46
- async def send(self, target: Union[Member, TextChannel], *args, **kwargs) -> None:
52
+ async def send(self, target: Union[Member, GuildChannel], *args, **kwargs) -> None:
47
53
  """
48
54
  Send at the target
49
55
  """
50
56
  self.__ctx = await target.send(*args, **kwargs)
51
57
 
52
58
  def add_items(self,
53
- *items: Union[Item, SelectMenu, Pagination]) -> "EasyModifiedViews":
59
+ *items: Union[EasyModifiedViews, Confirm, Pagination, Poll, SelectMenu, Item]) -> EasyModifiedViews:
54
60
  """
55
61
  Add all items in the View.
56
62
  """
57
63
 
58
64
  for ui in items:
59
65
 
60
- if type(ui).__name__ in ('SelectMenu', 'Pagination'):
66
+ if ui is None: # si l'ui n'est pas setup
67
+ continue
68
+
69
+ if type(ui).__name__ in ('SelectMenu', 'Pagination', 'Confirm', 'Poll', 'EasyModifiedViews'):
61
70
  for item in ui.get_view.items:
62
71
  self.add_items(item)
63
72
  self.set_callable(item.custom_id, _callable=ui.get_callable(item.custom_id))
@@ -118,7 +127,7 @@ class EasyModifiedViews(View):
118
127
  """
119
128
  set up a callable for items
120
129
  :param custom_ids: items IDs of the view
121
- :param _callable: The callable linked
130
+ :param _callable: The asynchronous callable linked. Take UI (Button, Select...) and Interaction parameters.
122
131
 
123
132
  **UI and Interaction parameter is required in callable function !**
124
133
 
@@ -230,7 +239,6 @@ class EasyModifiedViews(View):
230
239
 
231
240
  return True
232
241
 
233
-
234
242
  async def delete_items(self, clear_all: bool = False, *custom_ids: str) -> None:
235
243
  """
236
244
  Delete an item on the view
@@ -254,13 +262,25 @@ class EasyModifiedViews(View):
254
262
  Called if timeout view is finished
255
263
  """
256
264
  if self.__disabled_on_timeout:
257
- self.shutdown()
265
+ create_task(self.shutdown())
266
+ if self.__call_on_timeout is not None:
267
+ create_task(self.__call_on_timeout(self.__ctx))
268
+
269
+ def call_on_timeout(self, _callable: Callable) -> None:
270
+ """
271
+ Call asynchronous function passed when the timeout is reached.
272
+ :param _callable: asynchronous function to call. It takes one argument : ctx
273
+ """
274
+ if iscoroutinefunction(_callable):
275
+ self.__call_on_timeout = _callable
276
+ else:
277
+ raise CoroutineError(_callable)
258
278
 
259
279
  async def _update(self) -> None:
260
280
  """
261
281
  Update the View on the attached message.
262
282
  """
263
- if self.is_finished():
283
+ if self.is_finished() and not self.__disabled_on_timeout:
264
284
  return
265
285
 
266
286
  if self.message:
@@ -269,16 +289,6 @@ class EasyModifiedViews(View):
269
289
  elif self.__ctx:
270
290
  await self.__ctx.edit(view=self)
271
291
 
272
- else:
273
- return
274
-
275
- @property
276
- def get_uis(self) -> list[Item]:
277
- """
278
- Get all uis in the view
279
- """
280
- return [i['ui'] for i in self.__callback.values()]
281
-
282
292
  def get_ui(self, custom_id: str) -> Item:
283
293
  """
284
294
  Get an ui in the view
@@ -287,9 +297,39 @@ class EasyModifiedViews(View):
287
297
  self.__check_custom_id(custom_id)
288
298
  return self.__callback[custom_id]['ui']
289
299
 
300
+ def copy(self) -> EasyModifiedViews:
301
+ """
302
+ Return an exact copy of the view. All mutable object in the view is a new object
303
+ """
304
+ e = EasyModifiedViews(timeout=self.__timeout, disabled_on_timeout=self.__disabled_on_timeout).add_items(*self.items)
305
+ for i in self.items:
306
+ e.set_callable(i.custom_id, _callable=self.get_callable(i.custom_id))
307
+
308
+ return e
309
+
290
310
  def __str__(self):
291
311
  return str(self.__callback)
292
312
 
293
313
  @property
294
314
  def items(self) -> tuple[Item]:
295
315
  return tuple([i['ui'] for i in self.__callback.values()])
316
+
317
+ @property
318
+ def get_view(self) -> EasyModifiedViews:
319
+ """
320
+ Get The current view. Don't use in your code
321
+ """
322
+ return self
323
+
324
+ def __add__(self, _view: Union[EasyModifiedViews, Confirm, Pagination, Poll, SelectMenu, Item]) -> EasyModifiedViews:
325
+ """
326
+ Add all items to _view from the current EasyModifiedViews instance
327
+ """
328
+ self.add_items(_view)
329
+ return self
330
+
331
+ def __iadd__(self, _view) -> EasyModifiedViews:
332
+ """
333
+ Add all items to _view from the current EasyModifiedViews instance
334
+ """
335
+ return self.__add__(_view)
@@ -0,0 +1,13 @@
1
+ class Py_cordEasyModifiedViews(Exception):
2
+ """
3
+ Main class exception
4
+ """
5
+
6
+
7
+ class CustomIDNotFound(Py_cordEasyModifiedViews):
8
+ def __init__(self):
9
+ super().__init__(f"custom_id not found !")
10
+
11
+ class CoroutineError(Py_cordEasyModifiedViews):
12
+ def __init__(self, coro):
13
+ super().__init__(f"{coro} is not a coroutine !")
@@ -5,7 +5,7 @@ with open("README.md", encoding='utf-8') as file:
5
5
 
6
6
  setup(
7
7
  name="PycordViews",
8
- version="1.2.4",
8
+ version="1.2.6",
9
9
  url="https://github.com/BOXERRMD/Py-cord_Views",
10
10
  author="Chronos (alias BOXERRMD)",
11
11
  author_email="vagabonwalybi@gmail.com",
@@ -27,6 +27,6 @@ setup(
27
27
  install_requires=[
28
28
  "immutable-Python-type"
29
29
  ],
30
- packages=['pycordViews', 'pycordViews/pagination', 'pycordViews/views', 'pycordViews/menu', 'pycordViews/multibot'],
30
+ packages=['pycordViews', 'pycordViews/pagination', 'pycordViews/views', 'pycordViews/menu', 'pycordViews/multibot', 'pycordViews/kit'],
31
31
  python_requires='>=3.9'
32
32
  )
@@ -1,9 +0,0 @@
1
- class Py_cordEasyModifiedViews(Exception):
2
- """
3
- Main class exception
4
- """
5
-
6
-
7
- class CustomIDNotFound(Py_cordEasyModifiedViews):
8
- def __init__(self):
9
- super().__init__(f"custom_id not found !")
File without changes
File without changes
File without changes
File without changes