componentsv2 0.1.0__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.
- componentsv2-0.1.0/LICENSE.txt +21 -0
- componentsv2-0.1.0/PKG-INFO +9 -0
- componentsv2-0.1.0/README.md +2 -0
- componentsv2-0.1.0/componentsv2/__init__.py +11 -0
- componentsv2-0.1.0/componentsv2/component_holder.py +212 -0
- componentsv2-0.1.0/componentsv2/wrapper.py +103 -0
- componentsv2-0.1.0/componentsv2.egg-info/PKG-INFO +9 -0
- componentsv2-0.1.0/componentsv2.egg-info/SOURCES.txt +12 -0
- componentsv2-0.1.0/componentsv2.egg-info/dependency_links.txt +1 -0
- componentsv2-0.1.0/componentsv2.egg-info/requires.txt +1 -0
- componentsv2-0.1.0/componentsv2.egg-info/top_level.txt +1 -0
- componentsv2-0.1.0/setup.cfg +7 -0
- componentsv2-0.1.0/setup.py +10 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 finnagen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
from .components.base import ComponentV2
|
|
2
|
+
from .components.button import ButtonV2
|
|
3
|
+
from .components.action_row import ActionRow
|
|
4
|
+
from .components.select import SelectObject
|
|
5
|
+
|
|
6
|
+
import copy
|
|
7
|
+
|
|
8
|
+
class ComponentHolder():
|
|
9
|
+
def __init_subclass__(cls):
|
|
10
|
+
children: list[ComponentV2] = []
|
|
11
|
+
|
|
12
|
+
for base in reversed(cls.__mro__):
|
|
13
|
+
children.extend(
|
|
14
|
+
member
|
|
15
|
+
for member in base.__dict__.values()
|
|
16
|
+
if hasattr(member, "__discord_ui_model_type__")
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
if len(children) > 25:
|
|
20
|
+
raise TypeError("Component Holders can only contain 25 components!")
|
|
21
|
+
|
|
22
|
+
cls.children = children
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
self.children = copy.deepcopy(self.children)
|
|
26
|
+
rows: list[ActionRow] = []
|
|
27
|
+
|
|
28
|
+
for child in self.children:
|
|
29
|
+
if hasattr(child, "parent_holder") == False:
|
|
30
|
+
child.parent_holder = self
|
|
31
|
+
|
|
32
|
+
if isinstance(child, ActionRow):
|
|
33
|
+
rows.append(child)
|
|
34
|
+
|
|
35
|
+
self.rows = rows
|
|
36
|
+
|
|
37
|
+
for child in self.children:
|
|
38
|
+
if isinstance(child, ButtonV2) or isinstance(child, SelectObject): # switch StringSelect to general Select object
|
|
39
|
+
self.append_to_row(child.row, child)
|
|
40
|
+
|
|
41
|
+
def __rec_add_components(self, component: ComponentV2, initial: bool):
|
|
42
|
+
if hasattr(component, "row"):
|
|
43
|
+
self.append_to_row(component.row, component)
|
|
44
|
+
|
|
45
|
+
if hasattr(component, "parent_holder") == False:
|
|
46
|
+
component.parent_holder = self
|
|
47
|
+
elif isinstance(component.parent_holder, ComponentHolder):
|
|
48
|
+
raise TypeError("Cannot re-assign components!")
|
|
49
|
+
|
|
50
|
+
if hasattr(component, "accessory") == True:
|
|
51
|
+
component.accessory.__row_child__ = True
|
|
52
|
+
self.children.append(component.accessory)
|
|
53
|
+
|
|
54
|
+
if initial == False:
|
|
55
|
+
component.__row_child__ = True
|
|
56
|
+
|
|
57
|
+
self.children.append(component)
|
|
58
|
+
|
|
59
|
+
if hasattr(component, "components") == True:
|
|
60
|
+
for child in component.components:
|
|
61
|
+
self.__rec_add_components(child, False)
|
|
62
|
+
|
|
63
|
+
def add_component(self, component: ComponentV2):
|
|
64
|
+
self.__rec_add_components(component, True)
|
|
65
|
+
return self
|
|
66
|
+
|
|
67
|
+
def add_components(self, *args: ComponentV2):
|
|
68
|
+
components = list(args)
|
|
69
|
+
|
|
70
|
+
for component in components:
|
|
71
|
+
self.__rec_add_components(component, True)
|
|
72
|
+
|
|
73
|
+
def append_to_row(self, row: int, component: ComponentV2):
|
|
74
|
+
if row < len(self.rows):
|
|
75
|
+
action_row = self.rows[row]
|
|
76
|
+
elif row == len(self.rows):
|
|
77
|
+
action_row = ActionRow()
|
|
78
|
+
self.rows.append(action_row)
|
|
79
|
+
self.children.append(action_row)
|
|
80
|
+
else:
|
|
81
|
+
raise IndexError("New rows must be created sequentially (cannot skip rows).")
|
|
82
|
+
|
|
83
|
+
component.__row_child__ = True
|
|
84
|
+
action_row.append_component(component)
|
|
85
|
+
|
|
86
|
+
def serialize(self):
|
|
87
|
+
components = []
|
|
88
|
+
|
|
89
|
+
for child in self.children:
|
|
90
|
+
if hasattr(child, "__row_child__") == True:
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
components.append(child.serialize())
|
|
94
|
+
|
|
95
|
+
return components
|
|
96
|
+
|
|
97
|
+
def get_component(self, custom_id: str):
|
|
98
|
+
for child in self.children:
|
|
99
|
+
if getattr(child, "custom_id", None) == custom_id:
|
|
100
|
+
return child
|
|
101
|
+
|
|
102
|
+
class ModalV2():
|
|
103
|
+
def __init__(self, custom_id: str, title: str):
|
|
104
|
+
if custom_id == None:
|
|
105
|
+
raise ValueError("With this wrapper, custom_ids for modals are required!")
|
|
106
|
+
|
|
107
|
+
self.custom_id = custom_id
|
|
108
|
+
self.title = title
|
|
109
|
+
|
|
110
|
+
self.children = []
|
|
111
|
+
|
|
112
|
+
async def __handle_component_submitted(self, sc: dict[str]):
|
|
113
|
+
if custom_id := sc.get("custom_id"):
|
|
114
|
+
component = self.get_component(custom_id)
|
|
115
|
+
|
|
116
|
+
if component is not None:
|
|
117
|
+
if values := sc.get("values"):
|
|
118
|
+
component.values = values
|
|
119
|
+
else:
|
|
120
|
+
values = None
|
|
121
|
+
|
|
122
|
+
if (value := sc.get("value")) is not None:
|
|
123
|
+
component.value = value
|
|
124
|
+
elif values:
|
|
125
|
+
component.value = values[0]
|
|
126
|
+
|
|
127
|
+
if component := sc.get("component"):
|
|
128
|
+
await self.__handle_component_submitted(component)
|
|
129
|
+
|
|
130
|
+
if components := sc.get("components"):
|
|
131
|
+
for child in components:
|
|
132
|
+
await self.__handle_component_submitted(child)
|
|
133
|
+
|
|
134
|
+
async def submitted(self, interaction, ser_components: list[dict[str]], initial: bool = True):
|
|
135
|
+
for sc in ser_components:
|
|
136
|
+
await self.__handle_component_submitted(sc)
|
|
137
|
+
|
|
138
|
+
if initial == True:
|
|
139
|
+
await self.on_form_submit(interaction)
|
|
140
|
+
|
|
141
|
+
async def on_form_submit(self, interaction):
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
def __rec_add_components(self, component: ComponentV2, initial: bool):
|
|
145
|
+
if hasattr(component, "parent_holder") == False:
|
|
146
|
+
component.parent_holder = self
|
|
147
|
+
elif isinstance(component.parent_holder, ModalV2):
|
|
148
|
+
raise TypeError("Cannot re-assign components!")
|
|
149
|
+
|
|
150
|
+
if hasattr(component, "accessory") == True:
|
|
151
|
+
component.accessory.__row_child__ = True
|
|
152
|
+
self.children.append(component.accessory)
|
|
153
|
+
|
|
154
|
+
if initial == False:
|
|
155
|
+
component.__row_child__ = True
|
|
156
|
+
|
|
157
|
+
self.children.append(component)
|
|
158
|
+
|
|
159
|
+
if hasattr(component, "components") == True:
|
|
160
|
+
for child in component.components:
|
|
161
|
+
self.__rec_add_components(child, False)
|
|
162
|
+
|
|
163
|
+
if hasattr(component, "component") == True:
|
|
164
|
+
self.__rec_add_components(component.component, False)
|
|
165
|
+
|
|
166
|
+
def add_component(self, component: ComponentV2):
|
|
167
|
+
self.__rec_add_components(component, True)
|
|
168
|
+
return self
|
|
169
|
+
|
|
170
|
+
def add_components(self, *args: ComponentV2):
|
|
171
|
+
components = list(args)
|
|
172
|
+
|
|
173
|
+
for component in components:
|
|
174
|
+
self.__rec_add_components(component, True)
|
|
175
|
+
|
|
176
|
+
def serialize(self):
|
|
177
|
+
components = []
|
|
178
|
+
|
|
179
|
+
for child in self.children:
|
|
180
|
+
if hasattr(child, "__row_child__") == True:
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
components.append(child.serialize())
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
"type": 9,
|
|
187
|
+
"data": {
|
|
188
|
+
"custom_id": self.custom_id,
|
|
189
|
+
"title": self.title,
|
|
190
|
+
"components": components
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
def get_component(self, custom_id: str):
|
|
195
|
+
if custom_id == None:
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
for child in self.children:
|
|
199
|
+
if getattr(child, "custom_id", None) == custom_id:
|
|
200
|
+
return child
|
|
201
|
+
|
|
202
|
+
def get_components(self) -> list[ComponentV2]:
|
|
203
|
+
components = []
|
|
204
|
+
|
|
205
|
+
for child in self.children:
|
|
206
|
+
if hasattr(child, "__row_child__") == True:
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
components.append(child)
|
|
210
|
+
|
|
211
|
+
return components
|
|
212
|
+
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import nextcord
|
|
2
|
+
import json
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
from nextcord.ext import commands
|
|
6
|
+
from nextcord import Interaction
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from nextcord import MessageFlags
|
|
10
|
+
|
|
11
|
+
## CLASSES
|
|
12
|
+
from .components.base import ComponentV2
|
|
13
|
+
from .components.action_row import ActionRow
|
|
14
|
+
from .components.button import ButtonV2
|
|
15
|
+
|
|
16
|
+
from .components.select import (
|
|
17
|
+
StringSelect,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .component_holder import (
|
|
21
|
+
ComponentHolder,
|
|
22
|
+
ModalV2,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from .components.holders.label import Label
|
|
26
|
+
|
|
27
|
+
class NextcordAPIWrapperV2():
|
|
28
|
+
def __init__(self, bot: commands.Bot):
|
|
29
|
+
self.bot = bot
|
|
30
|
+
|
|
31
|
+
self.active_holders = {}
|
|
32
|
+
self.active_modals = {}
|
|
33
|
+
|
|
34
|
+
bot.add_listener(self.on_interaction, "on_interaction")
|
|
35
|
+
|
|
36
|
+
async def on_interaction(self, interaction: Interaction):
|
|
37
|
+
if interaction.type == nextcord.InteractionType.component:
|
|
38
|
+
holder: ComponentHolder = self.active_holders.get(str(interaction.message.id))
|
|
39
|
+
if holder != None:
|
|
40
|
+
component = holder.get_component(interaction.data.get("custom_id"))
|
|
41
|
+
if component != None:
|
|
42
|
+
await component.activated(interaction)
|
|
43
|
+
elif interaction.type == nextcord.InteractionType.modal_submit:
|
|
44
|
+
modal: ModalV2 = self.active_modals.get(interaction.user.id)
|
|
45
|
+
if modal != None:
|
|
46
|
+
await modal.submitted(interaction, interaction.data.get("components"))
|
|
47
|
+
|
|
48
|
+
def __message_interaction_payload(self, components: list[ComponentV2], content: str = None, flags: int = None) -> dict[str]:
|
|
49
|
+
return {
|
|
50
|
+
"type": 4, # update your FUCKING docs discord
|
|
51
|
+
"data": {
|
|
52
|
+
"flags": flags,
|
|
53
|
+
"content": content,
|
|
54
|
+
"components": components
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
def __edit_interaction_payload(self, components: list[ComponentV2], content: str = None) -> dict[str]:
|
|
59
|
+
return {
|
|
60
|
+
"type": 7, # update your FUCKING docs discord
|
|
61
|
+
"data": {
|
|
62
|
+
"content": content,
|
|
63
|
+
"components": components
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async def send_modal(self, interaction: Interaction, modal: ModalV2):
|
|
68
|
+
self.active_modals[interaction.user.id] = modal
|
|
69
|
+
|
|
70
|
+
await self.bot.http.request(
|
|
71
|
+
nextcord.http.Route("POST", f"/interactions/{interaction.id}/{interaction.token}/callback"),
|
|
72
|
+
json = modal.serialize()
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
async def send_message(self, interaction: Interaction, component_holder: ComponentHolder, content: str = None, *, ephemeral: bool = False, is_components_v2: bool = False):
|
|
76
|
+
if is_components_v2 == True and content != None:
|
|
77
|
+
raise TypeError("ComponentsV2 messages cannot contain content!")
|
|
78
|
+
|
|
79
|
+
flags = MessageFlags(ephemeral=ephemeral, is_components_v2=is_components_v2).value
|
|
80
|
+
response = await self.bot.http.request(
|
|
81
|
+
nextcord.http.Route("POST", f"/interactions/{interaction.id}/{interaction.token}/callback?with_response=true"),
|
|
82
|
+
json=self.__message_interaction_payload(components=component_holder.serialize(), content=content, flags=flags)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
self.active_holders[response["interaction"]["response_message_id"]] = component_holder
|
|
86
|
+
|
|
87
|
+
async def send_followup(self, interaction: Interaction, component_holder: ComponentHolder, content: str = None, *, ephemeral: bool = False, is_components_v2: bool = False):
|
|
88
|
+
if is_components_v2 == True and content != None:
|
|
89
|
+
raise TypeError("ComponentsV2 messages cannot contain content!")
|
|
90
|
+
|
|
91
|
+
flags = MessageFlags(ephemeral=ephemeral, is_components_v2=is_components_v2).value
|
|
92
|
+
response = await self.bot.http.request(
|
|
93
|
+
nextcord.http.Route("POST", f"/webhooks/{interaction.application_id}/{interaction.token}"),
|
|
94
|
+
json={"components": component_holder.serialize(), "content": content, "flags": flags}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
self.active_holders[response["id"]] = component_holder
|
|
98
|
+
|
|
99
|
+
async def edit_message(self, interaction: Interaction, component_holder: ComponentHolder, content: str = None):
|
|
100
|
+
await self.bot.http.request(
|
|
101
|
+
nextcord.http.Route("POST", f"/interactions/{interaction.id}/{interaction.token}/callback"),
|
|
102
|
+
json=self.__edit_interaction_payload(components=component_holder.serialize(), content=content)
|
|
103
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
README.md
|
|
3
|
+
setup.cfg
|
|
4
|
+
setup.py
|
|
5
|
+
componentsv2/__init__.py
|
|
6
|
+
componentsv2/component_holder.py
|
|
7
|
+
componentsv2/wrapper.py
|
|
8
|
+
componentsv2.egg-info/PKG-INFO
|
|
9
|
+
componentsv2.egg-info/SOURCES.txt
|
|
10
|
+
componentsv2.egg-info/dependency_links.txt
|
|
11
|
+
componentsv2.egg-info/requires.txt
|
|
12
|
+
componentsv2.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nextcord
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
componentsv2
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
setup(
|
|
3
|
+
name = 'componentsv2', # How you named your package folder (MyLib)
|
|
4
|
+
packages = ['componentsv2'], # Chose the same as "name"
|
|
5
|
+
version = '0.1.0', # Start with a small number and increase it with every change you make
|
|
6
|
+
license='MIT',
|
|
7
|
+
install_requires=[
|
|
8
|
+
"nextcord",
|
|
9
|
+
]
|
|
10
|
+
)
|