Skip to content

Commit

Permalink
Introduce Kumiko's help command + pages
Browse files Browse the repository at this point in the history
  • Loading branch information
No767 committed Jul 14, 2023
1 parent c69196c commit 6338ea7
Show file tree
Hide file tree
Showing 18 changed files with 825 additions and 58 deletions.
3 changes: 3 additions & 0 deletions bot/cogs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from pkgutil import iter_modules

EXTENSIONS = [module.name for module in iter_modules(__path__, f"{__package__}.")]
40 changes: 22 additions & 18 deletions bot/cogs/xelt.py → bot/cogs/meta.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,47 @@

import discord
import psutil
from discord import app_commands
from discord.ext import commands
from xeltcore import XeltCore

BUILD_VERSION = "v3.0.0-dev"


class Xelt(commands.Cog):
"""Get Xelt to your server!"""
class Meta(commands.Cog):
"""Provides metadata about Xelt"""

def __init__(self, bot):
def __init__(self, bot: XeltCore) -> None:
self.bot = bot

@property
def display_emoji(self) -> discord.PartialEmoji:
return discord.PartialEmoji(name="\U00002754")

@commands.Cog.listener()
async def on_ready(self):
global startTime
startTime = time.time()

@app_commands.command(name="support")
async def getSupport(self, interaction: discord.Interaction):
@commands.hybrid_command(name="support")
async def getSupport(self, ctx: commands.Context):
"""Need some help?"""
embed = discord.Embed(color=discord.Color.from_rgb(133, 255, 159))
embed.title = "Need some help?"
embed.description = (
"Feel free to join our server [here](https://discord.gg/e95ct6s5Gz)"
)
await interaction.response.send_message(embed=embed, ephemeral=True)
await ctx.send(embed=embed, ephemeral=True)

@app_commands.command(name="invite")
async def getInvite(self, interaction: discord.Interaction):
@commands.hybrid_command(name="invite")
async def getInvite(self, ctx: commands.Context):
"""Invite me to your server!"""
embed = discord.Embed(color=discord.Color.from_rgb(19, 191, 0))
embed.title = "Invite me!"
embed.description = "Want to invite Xelt to your own server?\nInvite me [here](https://discord.com/api/oauth2/authorize?client_id=726763157195849728&permissions=414501563457&scope=bot) and enjoy Xelt's features in your very own server!"
await interaction.response.send_message(embed=embed, ephemeral=True)
await ctx.send(embed=embed, ephemeral=True)

@app_commands.command(name="vote")
async def getVote(self, interaction: discord.Interaction):
@commands.hybrid_command(name="vote")
async def getVote(self, ctx: commands.Context):
"""Vote our bot!"""
embed = discord.Embed(color=discord.Color.from_rgb(255, 145, 244))
embed.title = "Vote us!"
Expand All @@ -53,10 +57,10 @@ async def getVote(self, interaction: discord.Interaction):
name="Discord Bot List",
value="[Click here](https://discordbotlist.com/bots/xelt)",
)
await interaction.response.send_message(embed=embed, ephemeral=True)
await ctx.send(embed=embed, ephemeral=True)

@app_commands.command(name="stats")
async def getStats(self, interaction: discord.Interaction):
@commands.hybrid_command(name="stats")
async def getStats(self, ctx: commands.Context):
"""Show bot stats"""
# Note that the calculation of the guilds and members may or may not take quite some time
# discord.py should automatically cache how much guilds and members a bot is in
Expand All @@ -81,8 +85,8 @@ async def getStats(self, interaction: discord.Interaction):
name="System",
value=f"```💻 CPU [{os.cpu_count()} Cores]\n🎞 Memory [{freeMemory:.2f} GB / {totalMemory:.0f} GB] ```",
)
await interaction.response.send_message(embed=embed, ephemeral=True)
await ctx.send(embed=embed, ephemeral=True)


async def setup(bot):
await bot.add_cog(Xelt(bot))
async def setup(bot: XeltCore) -> None:
await bot.add_cog(Meta(bot))
24 changes: 0 additions & 24 deletions bot/cogs/typerace.py

This file was deleted.

3 changes: 2 additions & 1 deletion bot/libs/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .checks import check_db_servers
from .embeds import Embed
from .utils import encodeDatetime, parseDatetime
from .xelt_logger import XeltLogger

__all__ = ["check_db_servers", "parseDatetime", "encodeDatetime", "XeltLogger"]
__all__ = ["check_db_servers", "Embed", "parseDatetime", "encodeDatetime", "XeltLogger"]
9 changes: 9 additions & 0 deletions bot/libs/utils/embeds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import discord


class Embed(discord.Embed):
"""Xelt's custom default embed"""

def __init__(self, **kwargs):
kwargs.setdefault("color", discord.Color.from_rgb(255, 163, 253))
super().__init__(**kwargs)
3 changes: 3 additions & 0 deletions bot/libs/utils/help/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .xelt_help import XeltHelp

__all__ = ["XeltHelp"]
121 changes: 121 additions & 0 deletions bot/libs/utils/help/sources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import inspect
from typing import Any, List, Optional, Union

import discord
from discord.ext import commands, menus
from libs.utils.pages import XeltPages

from .ui import HelpMenu


class GroupHelpPageSource(menus.ListPageSource):
def __init__(
self,
group: Union[commands.Group, commands.Cog],
entries: List[commands.Command],
*,
prefix: str,
):
super().__init__(entries=entries, per_page=6)
self.group: Union[commands.Group, commands.Cog] = group
self.prefix: str = prefix
self.title: str = f"{self.group.qualified_name} Commands"
self.description: str = self.group.description

async def format_page(self, menu: XeltPages, commands: List[commands.Command]):
embed = discord.Embed(
title=self.title,
description=self.description,
colour=discord.Colour.from_rgb(197, 184, 255),
)

for command in commands:
signature = f"{command.qualified_name} {command.signature}"
embed.add_field(
name=signature,
value=command.short_doc or "No help given...",
inline=False,
)

maximum = self.get_max_pages()
if maximum > 1:
embed.set_author(
name=f"Page {menu.current_page + 1}/{maximum} ({len(self.entries)} commands)"
)

embed.set_footer(
text=f'Use "{self.prefix}help command" for more info on a command.'
)
return embed


class FrontPageSource(menus.PageSource):
def is_paginating(self) -> bool:
# This forces the buttons to appear even in the front page
return True

def get_max_pages(self) -> Optional[int]:
# There's only one actual page in the front page
# However we need at least 2 to show all the buttons
return 2

async def get_page(self, page_number: int) -> Any:
# The front page is a dummy
self.index = page_number
return self

def format_page(self, menu: HelpMenu, page: Any):
embed = discord.Embed(
title="Bot Help", colour=discord.Colour.from_rgb(255, 161, 231)
)
# embed.description = "help"
embed.description = inspect.cleandoc(
f"""
Hello! Welcome to the help page.
Use "{menu.ctx.clean_prefix}help command" for more info on a command.
Use "{menu.ctx.clean_prefix}help category" for more info on a category.
Use the dropdown menu below to select a category.
"""
)

embed.add_field(
name="Support Server",
value="For more help, consider joining the official server over at https://discord.gg/sYP7z2sUda",
inline=False,
)

# created_at = time.format_dt(menu.ctx.bot.user.created_at, 'F')
if self.index == 0:
embed.add_field(
name="About Kumiko",
value=(
"Xelt is an multipurpose bot with a wide variety of commands and features. You may be wondering, "
"what an multipurpose bot is. Xelt offers features such as the famous typeracer game, custom prefix, and many more. You can get more "
"information on the commands offered by using the dropdown below.\n\n"
"Xelt is also open source. You can see the code on [GitHub](https://github.com/XeltBot/Xelt.py)"
),
inline=False,
)
elif self.index == 1:
entries = (
("<argument>", "This means the argument is __**required**__."),
("[argument]", "This means the argument is __**optional**__."),
("[A|B]", "This means that it can be __**either A or B**__."),
(
"[argument...]",
"This means you can have multiple arguments.\n"
"Now that you know the basics, it should be noted that...\n"
"__**You do not type in the brackets!**__",
),
)

embed.add_field(
name="How do I use this bot?",
value="Reading the bot signature is pretty simple.",
)

for name, value in entries:
embed.add_field(name=name, value=value, inline=False)

return embed
88 changes: 88 additions & 0 deletions bot/libs/utils/help/ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from typing import Dict, List

import discord
from discord.ext import commands, menus
from libs.utils.pages import XeltPages

from .sources import FrontPageSource, GroupHelpPageSource


class HelpSelectMenu(discord.ui.Select["HelpMenu"]):
def __init__(self, entries: Dict[commands.Cog, List[commands.Command]], bot):
super().__init__(
placeholder="Select a category...",
min_values=1,
max_values=1,
row=0,
)
self.cmds: dict[commands.Cog, List[commands.Command]] = entries
self.bot = bot
self.__fill_options()

def __fill_options(self) -> None:
self.add_option(
label="Index",
emoji="\N{WAVING HAND SIGN}",
value="__index",
description="The help page showing how to use the bot.",
)
for cog, cmds in self.cmds.items():
if not cmds:
continue
description = cog.description.split("\n", 1)[0] or None
emoji = getattr(cog, "display_emoji", None)
self.add_option(
label=cog.qualified_name,
value=cog.qualified_name,
description=description,
emoji=emoji,
)

async def callback(self, interaction: discord.Interaction):
assert self.view is not None
value = self.values[0]
if value == "__index":
await self.view.rebind(FrontPageSource(), interaction)
else:
cog = self.bot.get_cog(value)
if cog is None:
await interaction.response.send_message(
"Somehow this category does not exist?", ephemeral=True
)
return

commands = self.cmds[cog]
if not commands:
await interaction.response.send_message(
"This category has no commands for you", ephemeral=True
)
return

source = GroupHelpPageSource(
cog, commands, prefix=self.view.ctx.clean_prefix
)
await self.view.rebind(source, interaction)


class HelpMenu(XeltPages):
def __init__(self, source: menus.PageSource, ctx: commands.Context):
super().__init__(source, ctx=ctx, compact=True)

def add_categories(
self, commands: Dict[commands.Cog, List[commands.Command]]
) -> None:
self.clear_items()
self.add_item(HelpSelectMenu(commands, self.ctx.bot))
self.fill_items()

async def rebind(
self, source: menus.PageSource, interaction: discord.Interaction
) -> None:
self.source = source
self.current_page = 0

await self.source._prepare_once()
page = await self.source.get_page(0)
kwargs = await self._get_kwargs_from_page(page)
self._update_labels(0)
await interaction.response.edit_message(**kwargs, view=self)
Loading

0 comments on commit 6338ea7

Please sign in to comment.