Create help command implementation

pull/132/head
stijndcl 2022-09-19 01:28:18 +02:00
parent bf28611ddc
commit c5317b5d27
3 changed files with 139 additions and 22 deletions

View File

@ -25,7 +25,7 @@ class Currency(commands.Cog):
super().__init__() super().__init__()
self.client = client self.client = client
@commands.command(name="Award") @commands.command(name="award")
@commands.check(is_owner) @commands.check(is_owner)
async def award( async def award(
self, self,
@ -38,11 +38,11 @@ class Currency(commands.Cog):
await crud.add_dinks(session, user.id, amount) await crud.add_dinks(session, user.id, amount)
plural = pluralize("Didier Dink", amount) plural = pluralize("Didier Dink", amount)
await ctx.reply( await ctx.reply(
f"**{ctx.author.display_name}** heeft **{user.display_name}** **{amount}** {plural} geschonken.", f"**{ctx.author.display_name}** has awarded **{user.display_name}** **{amount}** {plural}.",
mention_author=False, mention_author=False,
) )
@commands.group(name="bank", aliases=["B"], case_insensitive=True, invoke_without_command=True) @commands.group(name="bank", aliases=["b"], case_insensitive=True, invoke_without_command=True)
async def bank(self, ctx: commands.Context): async def bank(self, ctx: commands.Context):
"""Show your Didier Bank information""" """Show your Didier Bank information"""
async with self.client.postgres_session as session: async with self.client.postgres_session as session:
@ -57,7 +57,7 @@ class Currency(commands.Cog):
await ctx.reply(embed=embed, mention_author=False) await ctx.reply(embed=embed, mention_author=False)
@bank.group(name="Upgrade", aliases=["U", "Upgrades"], case_insensitive=True, invoke_without_command=True) @bank.group(name="upgrade", aliases=["u", "upgrades"], case_insensitive=True, invoke_without_command=True)
async def bank_upgrades(self, ctx: commands.Context): async def bank_upgrades(self, ctx: commands.Context):
"""List the upgrades you can buy & their prices""" """List the upgrades you can buy & their prices"""
async with self.client.postgres_session as session: async with self.client.postgres_session as session:
@ -77,7 +77,7 @@ class Currency(commands.Cog):
await ctx.reply(embed=embed, mention_author=False) await ctx.reply(embed=embed, mention_author=False)
@bank_upgrades.command(name="Capacity", aliases=["C"]) @bank_upgrades.command(name="capacity", aliases=["c"])
async def bank_upgrade_capacity(self, ctx: commands.Context): async def bank_upgrade_capacity(self, ctx: commands.Context):
"""Upgrade the capacity level of your bank""" """Upgrade the capacity level of your bank"""
async with self.client.postgres_session as session: async with self.client.postgres_session as session:
@ -88,7 +88,7 @@ class Currency(commands.Cog):
await ctx.reply("You don't have enough Didier Dinks to do this.", mention_author=False) await ctx.reply("You don't have enough Didier Dinks to do this.", mention_author=False)
await self.client.reject_message(ctx.message) await self.client.reject_message(ctx.message)
@bank_upgrades.command(name="Interest", aliases=["I"]) @bank_upgrades.command(name="interest", aliases=["i"])
async def bank_upgrade_interest(self, ctx: commands.Context): async def bank_upgrade_interest(self, ctx: commands.Context):
"""Upgrade the interest level of your bank""" """Upgrade the interest level of your bank"""
async with self.client.postgres_session as session: async with self.client.postgres_session as session:
@ -99,7 +99,7 @@ class Currency(commands.Cog):
await ctx.reply("You don't have enough Didier Dinks to do this.", mention_author=False) await ctx.reply("You don't have enough Didier Dinks to do this.", mention_author=False)
await self.client.reject_message(ctx.message) await self.client.reject_message(ctx.message)
@bank_upgrades.command(name="Rob", aliases=["R"]) @bank_upgrades.command(name="rob", aliases=["r"])
async def bank_upgrade_rob(self, ctx: commands.Context): async def bank_upgrade_rob(self, ctx: commands.Context):
"""Upgrade the rob level of your bank""" """Upgrade the rob level of your bank"""
async with self.client.postgres_session as session: async with self.client.postgres_session as session:
@ -118,7 +118,7 @@ class Currency(commands.Cog):
plural = pluralize("Didier Dink", bank.dinks) plural = pluralize("Didier Dink", bank.dinks)
await ctx.reply(f"**{ctx.author.display_name}** has **{bank.dinks}** {plural}.", mention_author=False) await ctx.reply(f"**{ctx.author.display_name}** has **{bank.dinks}** {plural}.", mention_author=False)
@commands.command(name="Invest", aliases=["Deposit", "Dep"]) @commands.command(name="invest", aliases=["deposit", "dep"])
async def invest(self, ctx: commands.Context, amount: typing.Annotated[typing.Union[str, int], abbreviated_number]): async def invest(self, ctx: commands.Context, amount: typing.Annotated[typing.Union[str, int], abbreviated_number]):
"""Invest a given amount of Didier Dinks""" """Invest a given amount of Didier Dinks"""
async with self.client.postgres_session as session: async with self.client.postgres_session as session:

View File

@ -1,10 +1,11 @@
from typing import List, Mapping, Optional from typing import Mapping, Optional
import discord import discord
from discord.ext import commands from discord.ext import commands
from overrides import overrides from overrides import overrides
from didier import Didier from didier import Didier
from didier.utils.discord.colours import error_red
class CustomHelpCommand(commands.MinimalHelpCommand): class CustomHelpCommand(commands.MinimalHelpCommand):
@ -13,32 +14,146 @@ class CustomHelpCommand(commands.MinimalHelpCommand):
The default is ugly as hell, so we do some fiddling with it The default is ugly as hell, so we do some fiddling with it
""" """
@overrides
async def command_callback(self, ctx: commands.Context, /, *, command: Optional[str] = None):
"""Slightly modify the original command_callback to better suit my needs"""
# No argument provided: send a list of all cogs
if command is None:
mapping = self.get_bot_mapping()
return await self.send_bot_help(mapping)
command = command.lower()
# Hide cogs the user is not allowed to see
cogs = list(ctx.bot.cogs.values())
cogs = await self._filter_cogs(cogs)
# Allow fetching cogs case-insensitively
cog = self._get_cog(cogs, command)
if cog is not None:
return await self.send_cog_help(cog)
# Traverse tree of commands
keys = command.split(" ")
current_command = ctx.bot.all_commands.get(keys[0])
# No command found
if current_command is None:
return await self.send_error_message(self.command_not_found(keys[0]))
# Look for subcommands
for key in keys[1:]:
try:
found = current_command.all_commands.get(key) # type: ignore
except AttributeError:
return await self.send_error_message(self.subcommand_not_found(current_command, key))
else:
if found is None:
return await self.send_error_message(self.subcommand_not_found(current_command, key))
current_command = found
if isinstance(current_command, commands.Group):
return await self.send_group_help(current_command)
else:
return await self.send_command_help(current_command)
@overrides
def command_not_found(self, string: str, /) -> str:
return f"Found no command named `{string}`."
@overrides
async def send_bot_help(self, mapping: Mapping[Optional[commands.Cog], list[commands.Command]], /):
embed = self._help_embed_base("Categories")
filtered_cogs = await self._filter_cogs(list(mapping.keys()))
embed.description = "\n".join(list(map(lambda cog: cog.qualified_name, filtered_cogs)))
await self.context.reply(embed=embed, mention_author=False)
@overrides
async def send_cog_help(self, cog: commands.Cog, /):
embed = self._help_embed_base(cog.qualified_name)
embed.description = cog.description
commands_names = list(map(lambda c: c.qualified_name, cog.get_commands()))
commands_names.sort()
embed.add_field(name="Commands", value=", ".join(commands_names), inline=False)
return await self.context.reply(embed=embed, mention_author=False)
@overrides
async def send_command_help(self, command: commands.Command, /):
embed = self._help_embed_base(command.qualified_name)
self._add_command_help(embed, command)
return await self.context.reply(embed=embed, mention_author=False)
@overrides
async def send_group_help(self, group: commands.Group, /):
embed = self._help_embed_base(group.qualified_name)
if group.invoke_without_command:
self._add_command_help(embed, group)
subcommand_names = list(map(lambda c: c.name, group.commands))
subcommand_names.sort()
embed.add_field(name="Subcommands", value=", ".join(subcommand_names))
return await self.context.reply(embed=embed, mention_author=False)
@overrides
async def send_error_message(self, error: str, /):
embed = discord.Embed(colour=error_red(), title="Help", description=error)
return await self.context.reply(embed=embed, mention_author=False)
@overrides
def subcommand_not_found(self, command: commands.Command, string: str, /) -> str:
return f"Found no subcommand named `{string}` for command `{command.qualified_name}`."
def _help_embed_base(self, title: str) -> discord.Embed: def _help_embed_base(self, title: str) -> discord.Embed:
"""Create the base structure for the embeds that get sent with the Help commands""" """Create the base structure for the embeds that get sent with the Help commands"""
embed = discord.Embed(title=title, colour=discord.Colour.blue()) embed = discord.Embed(title=title.title(), colour=discord.Colour.blue())
embed.set_footer(text="Syntax: Didier Help [Categorie] of Didier Help [Commando]")
return embed return embed
async def _filter_cogs(self, cogs: List[commands.Cog]) -> List[commands.Cog]: def _add_command_help(self, embed: discord.Embed, command: commands.Command):
"""Add command-related information to an embed
This allows re-using this logic for Group commands that can be invoked by themselves.
"""
embed.description = command.help
if command.usage:
embed.add_field(name="Signature", value=f"{command.name} {command.usage}", inline=False)
if command.aliases:
embed.add_field(name="Aliases", value=", ".join(command.aliases), inline=False)
def _get_cog(self, cogs: list[commands.Cog], name: str) -> Optional[commands.Cog]:
"""Try to find a cog, case-insensitively"""
for cog in cogs:
if cog.qualified_name.lower() == name:
return cog
return None
async def _filter_cogs(self, cogs: list[commands.Cog]) -> list[commands.Cog]:
"""Filter the list of cogs down to all those that the user can see""" """Filter the list of cogs down to all those that the user can see"""
# Remove cogs that we never want to see in the help page because they # Remove cogs that we never want to see in the help page because they
# don't contain commands # don't contain commands
filtered_cogs = list(filter(lambda cog: cog is not None and cog.qualified_name.lower() not in ("tasks",), cogs)) filtered_cogs = list(
filter(lambda cog: cog is not None and cog.qualified_name.lower() not in ("tasks", "debugcog"), cogs)
)
# Remove owner-only cogs # Remove owner-only cogs for people that shouldn't see them
if not await self.context.bot.is_owner(self.context.author): if not await self.context.bot.is_owner(self.context.author):
filtered_cogs = list(filter(lambda cog: cog.qualified_name.lower() not in ("owner",), filtered_cogs)) filtered_cogs = list(filter(lambda cog: cog.qualified_name.lower() not in ("owner",), filtered_cogs))
return list(sorted(filtered_cogs, key=lambda cog: cog.qualified_name)) return list(sorted(filtered_cogs, key=lambda cog: cog.qualified_name))
@overrides
async def send_bot_help(self, mapping: Mapping[Optional[commands.Cog], List[commands.Command]], /):
embed = self._help_embed_base("Categorieën")
filtered_cogs = await self._filter_cogs(list(mapping.keys()))
embed.description = "\n".join(list(map(lambda cog: cog.qualified_name, filtered_cogs)))
await self.get_destination().send(embed=embed)
async def setup(client: Didier): async def setup(client: Didier):
"""Load the cog""" """Load the cog"""
client.help_command = CustomHelpCommand() attributes = {"aliases": ["h", "man"]}
client.help_command = CustomHelpCommand(command_attrs=attributes)

View File

@ -190,8 +190,10 @@ class Tasks(commands.Cog):
await self.client.wait_until_ready() await self.client.wait_until_ready()
@check_birthdays.error @check_birthdays.error
@pull_schedules.error
@pull_ufora_announcements.error @pull_ufora_announcements.error
@remove_old_ufora_announcements.error @remove_old_ufora_announcements.error
@reset_wordle_word.error
async def _on_tasks_error(self, error: BaseException): async def _on_tasks_error(self, error: BaseException):
"""Error handler for all tasks""" """Error handler for all tasks"""
print("".join(traceback.format_exception(type(error), error, error.__traceback__))) print("".join(traceback.format_exception(type(error), error, error.__traceback__)))