Files
Mace-AIO-Discord-Bot---With…/bot/utils/help.py
TheOnlyMace b4110c3d66
Some checks failed
CI / Bot (Python) (push) Failing after 49s
CI / Dashboard (Next.js) (push) Failing after 11s
Update project configuration files, and API routes accordingly. Add SQLite runtime file ignores to .gitignore for better file management.
2026-07-21 17:11:38 +02:00

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)