226 lines
9.6 KiB
Python
226 lines
9.6 KiB
Python
# ╔══════════════════════════════════════════════════════════════════╗
|
|
# ║ ║
|
|
# ║ +-+-+-+-+-+-+-+-+ ║
|
|
# ║ |H|e|x|a|H|o|s|t| ║
|
|
# ║ +-+-+-+-+-+-+-+-+ ║
|
|
# ║ ║
|
|
# ║ © 2026 HexaHost — All Rights Reserved ║
|
|
# ║ ║
|
|
# ║ discord ── https://discord.gg/hexahost ║
|
|
# ║ github ── https://github.com/theoneandonlymace ║
|
|
# ║ ║
|
|
# ╚══════════════════════════════════════════════════════════════════╝
|
|
|
|
import discord
|
|
from utils.Tools import *
|
|
from utils.cv2 import build_container
|
|
from utils.emoji import REWIND, PREVIOUS, NEXT, FORWARD, DELETE, HOME
|
|
from discord.ui import LayoutView, TextDisplay, Separator, ActionRow
|
|
|
|
|
|
class Dropdown(discord.ui.Select):
|
|
|
|
def __init__(self, ctx, options, placeholder="Choose a Category for Help"):
|
|
super().__init__(placeholder=placeholder,
|
|
min_values=1,
|
|
max_values=1,
|
|
options=options)
|
|
self.invoker = ctx.author
|
|
|
|
async def callback(self, interaction: discord.Interaction):
|
|
if self.invoker == interaction.user:
|
|
index = self.view.find_index_from_select(self.values[0])
|
|
if not index:
|
|
index = 0
|
|
await self.view.set_page(index, interaction)
|
|
else:
|
|
await interaction.response.send_message(
|
|
"You must run this command to interact with it.", ephemeral=True)
|
|
|
|
|
|
class View(LayoutView):
|
|
|
|
def __init__(self, mapping: dict, ctx, homeembed, ui: int):
|
|
super().__init__(timeout=None)
|
|
self.mapping = mapping
|
|
self.ctx = ctx
|
|
self.index = 0
|
|
self.current_page = 0
|
|
self.ui = ui
|
|
|
|
self.options, self.pages, self.total_pages = self.gen_pages(homeembed)
|
|
self.pages[0]['footer'] = f"• Help page 1/{self.total_pages} | Requested by: {self.ctx.author.display_name}"
|
|
self._rebuild()
|
|
|
|
def _rebuild(self):
|
|
self.clear_items()
|
|
page = self.pages[self.index]
|
|
page['footer'] = f"• Help page {self.index + 1}/{self.total_pages} | Requested by: {self.ctx.author.display_name}"
|
|
|
|
# Build container items (text content)
|
|
items = []
|
|
if page.get('title'):
|
|
items.append(TextDisplay(f"**{page['title']}**"))
|
|
if page.get('description'):
|
|
if items:
|
|
items.append(Separator(visible=True))
|
|
items.append(TextDisplay(page['description']))
|
|
for name, value in page.get('fields', []):
|
|
items.append(Separator(visible=True))
|
|
items.append(TextDisplay(f"**{name}**\n{value}"))
|
|
|
|
# Build buttons
|
|
is_first = self.index == 0
|
|
is_last = self.index >= len(self.pages) - 1
|
|
|
|
homeB = discord.ui.Button(label="", emoji=REWIND, style=discord.ButtonStyle.secondary, disabled=is_first)
|
|
backB = discord.ui.Button(label="", emoji=PREVIOUS, style=discord.ButtonStyle.secondary, disabled=is_first)
|
|
quitB = discord.ui.Button(label="", emoji=DELETE, style=discord.ButtonStyle.danger)
|
|
nextB = discord.ui.Button(label="", emoji=NEXT, style=discord.ButtonStyle.secondary, disabled=is_last)
|
|
lastB = discord.ui.Button(label="", emoji=FORWARD, style=discord.ButtonStyle.secondary, disabled=is_last)
|
|
|
|
homeB.callback = self._home_cb
|
|
backB.callback = self._back_cb
|
|
quitB.callback = self._quit_cb
|
|
nextB.callback = self._next_cb
|
|
lastB.callback = self._last_cb
|
|
|
|
# Add buttons ActionRow inside the container
|
|
items.append(ActionRow(homeB, backB, quitB, nextB, lastB))
|
|
|
|
# Add dropdowns inside the container
|
|
if self.ui == 0:
|
|
items.append(ActionRow(Dropdown(ctx=self.ctx, options=self.options)))
|
|
elif self.ui == 2:
|
|
mid = len(self.options) // 2
|
|
o1, o2 = self.options[:mid], self.options[mid:]
|
|
if o1:
|
|
items.append(ActionRow(Dropdown(ctx=self.ctx, options=o1, placeholder="Main Commands")))
|
|
if o2:
|
|
items.append(ActionRow(Dropdown(ctx=self.ctx, options=o2, placeholder="Extra Commands")))
|
|
elif self.ui == 3:
|
|
items.append(ActionRow(Dropdown(ctx=self.ctx, options=self.options)))
|
|
|
|
# Add footer after controls
|
|
if page.get('footer'):
|
|
items.append(Separator(visible=True))
|
|
items.append(TextDisplay(f"*{page['footer']}*"))
|
|
|
|
# Build the single container with everything inside
|
|
self.add_item(build_container(*items))
|
|
|
|
async def _check(self, interaction):
|
|
if interaction.user != self.ctx.author:
|
|
await interaction.response.send_message("You must run this command to interact with it.", ephemeral=True)
|
|
return False
|
|
return True
|
|
|
|
async def _home_cb(self, interaction):
|
|
if await self._check(interaction):
|
|
await self.set_page(0, interaction)
|
|
|
|
async def _back_cb(self, interaction):
|
|
if await self._check(interaction):
|
|
await self.set_page(self.index - 1 if self.index > 0 else len(self.pages) - 1, interaction)
|
|
|
|
async def _quit_cb(self, interaction):
|
|
if await self._check(interaction):
|
|
await interaction.response.defer()
|
|
await interaction.delete_original_response()
|
|
|
|
async def _next_cb(self, interaction):
|
|
if await self._check(interaction):
|
|
await self.set_page(self.index + 1 if self.index < len(self.pages) - 1 else 0, interaction)
|
|
|
|
async def _last_cb(self, interaction):
|
|
if await self._check(interaction):
|
|
await self.set_page(len(self.pages) - 1, interaction)
|
|
|
|
def find_index_from_select(self, value):
|
|
i = 0
|
|
used_labels = set()
|
|
for cog in self.get_cogs():
|
|
if cog.__class__.__name__ == "Roleplay":
|
|
continue
|
|
if "help_custom" in dir(cog):
|
|
_, label, _ = cog.help_custom()
|
|
original_label = label
|
|
counter = 1
|
|
while label in used_labels:
|
|
label = f"{original_label} {counter}"
|
|
counter += 1
|
|
used_labels.add(label)
|
|
if label == value or value.startswith(original_label + " "):
|
|
return i + 1
|
|
i += 1
|
|
return 0
|
|
|
|
def get_cogs(self):
|
|
return list(self.mapping.keys())
|
|
|
|
def gen_pages(self, homeembed):
|
|
options, pages = [], []
|
|
total_pages = 0
|
|
used_labels = set()
|
|
|
|
options.append(discord.SelectOption(label="Home", emoji=HOME, description=""))
|
|
|
|
# Convert homeembed (CV2Embed) to page data
|
|
if hasattr(homeembed, '_title'):
|
|
home_page = {
|
|
'title': homeembed._title or '',
|
|
'description': homeembed._description or '',
|
|
'fields': list(homeembed._fields) if hasattr(homeembed, '_fields') else [],
|
|
'footer': None
|
|
}
|
|
else:
|
|
home_page = {
|
|
'title': getattr(homeembed, 'title', '') or '',
|
|
'description': getattr(homeembed, 'description', '') or '',
|
|
'fields': [(f.name, f.value) for f in homeembed.fields] if hasattr(homeembed, 'fields') and homeembed.fields else [],
|
|
'footer': homeembed.footer.text if hasattr(homeembed, 'footer') and homeembed.footer else None
|
|
}
|
|
|
|
pages.append(home_page)
|
|
total_pages += 1
|
|
used_labels.add("Home")
|
|
|
|
for cog in self.get_cogs():
|
|
if cog.__class__.__name__ == "Roleplay":
|
|
continue
|
|
if "help_custom" in dir(cog):
|
|
emoji, label, description = cog.help_custom()
|
|
original_label = label
|
|
counter = 1
|
|
while label in used_labels:
|
|
label = f"{original_label} {counter}"
|
|
counter += 1
|
|
used_labels.add(label)
|
|
options.append(discord.SelectOption(label=label, emoji=emoji, description=description))
|
|
|
|
fields = []
|
|
for command in cog.get_commands():
|
|
params = ""
|
|
for param in command.clean_params:
|
|
if param not in ["self", "ctx"]:
|
|
params += f" <{param}>"
|
|
help_text = command.help or "No description available"
|
|
if len(help_text) > 1020:
|
|
help_text = help_text[:1017] + "..."
|
|
fields.append((f"{command.name}{params}", f"{help_text}\n•"))
|
|
|
|
pages.append({
|
|
'title': f"{emoji} {original_label}",
|
|
'description': '',
|
|
'fields': fields,
|
|
'footer': None
|
|
})
|
|
total_pages += 1
|
|
|
|
return options, pages, total_pages
|
|
|
|
async def set_page(self, page, interaction):
|
|
self.index = page
|
|
self.current_page = page
|
|
self._rebuild()
|
|
await interaction.response.edit_message(view=self) |