1
Fork 0
mirror of https://github.com/wlinator/luminara.git synced 2024-10-02 20:23:12 +00:00

Merge pull request #23 from wlinator/moderation

Moderation: Kicks
This commit is contained in:
wlinator 2024-08-06 04:23:56 -04:00
commit bc0af82592
14 changed files with 135 additions and 189 deletions

View file

@ -109,8 +109,6 @@
"error_no_case_found_description": "no case found with that ID.", "error_no_case_found_description": "no case found with that ID.",
"error_no_private_message_author": "Guild Only", "error_no_private_message_author": "Guild Only",
"error_no_private_message_description": "this command can only be used in servers.", "error_no_private_message_description": "this command can only be used in servers.",
"error_not_allowed_in_channel_author": "Not Allowed In Channel",
"error_not_allowed_in_channel_description": "you can only use that command in {0}.",
"error_not_owner_author": "Owner Only", "error_not_owner_author": "Owner Only",
"error_not_owner_description": "this command requires Lumi ownership permissions.", "error_not_owner_description": "this command requires Lumi ownership permissions.",
"error_private_message_only_author": "Private Message Only", "error_private_message_only_author": "Private Message Only",
@ -168,6 +166,9 @@
"mod_warn_dm": "**{0}** you have been warned in `{1}`.\n\n**Reason:** `{2}`", "mod_warn_dm": "**{0}** you have been warned in `{1}`.\n\n**Reason:** `{2}`",
"mod_warned_author": "User Warned", "mod_warned_author": "User Warned",
"mod_warned_user": "user `{0}` has been warned.", "mod_warned_user": "user `{0}` has been warned.",
"mod_kicked_author": "User Kicked",
"mod_kicked_user": "user `{0}` has been kicked.",
"mod_kick_dm": "**{0}** you have been kicked from `{1}`.\n\n**Reason:** `{2}`",
"ping_author": "I'm online!", "ping_author": "I'm online!",
"ping_footer": "Latency: {0}ms", "ping_footer": "Latency: {0}ms",
"ping_pong": "Pong!", "ping_pong": "Pong!",
@ -189,5 +190,6 @@
"xp_lb_field_value": "level: **{0}**\nxp: `{1}/{2}`", "xp_lb_field_value": "level: **{0}**\nxp: `{1}/{2}`",
"xp_level": "Level {0}", "xp_level": "Level {0}",
"xp_progress": "Progress to next level", "xp_progress": "Progress to next level",
"xp_server_rank": "Server Rank: #{0}" "xp_server_rank": "Server Rank: #{0}",
"error_command_not_found": "No command called \"{0}\" found."
} }

View file

@ -66,13 +66,6 @@ async def on_command_error(ctx, error):
str(error), str(error),
) )
elif isinstance(error, LumiExceptions.NotAllowedInChannel):
author_text = CONST.STRINGS["error_not_allowed_in_channel_author"]
description = CONST.STRINGS["error_not_allowed_in_channel_description"].format(
error.command_channel.mention,
)
ephemeral = True
else: else:
author_text = CONST.STRINGS["error_unknown_error_author"] author_text = CONST.STRINGS["error_unknown_error_author"]
description = CONST.STRINGS["error_unknown_error_description"] description = CONST.STRINGS["error_unknown_error_description"]
@ -111,8 +104,6 @@ class ErrorListener(Cog):
@Cog.listener() @Cog.listener()
async def on_command_error(self, ctx, error) -> None: async def on_command_error(self, ctx, error) -> None:
if isinstance(error, LumiExceptions.NotAllowedInChannel):
return
await on_command_error(ctx, error) await on_command_error(ctx, error)
await self.log_command_error(ctx, error, ".") await self.log_command_error(ctx, error, ".")

View file

@ -17,25 +17,3 @@ def birthdays_enabled():
return True return True
return commands.check(predicate) return commands.check(predicate)
def allowed_in_channel():
async def predicate(ctx):
if ctx.guild is None:
return True
guild_config = GuildConfig(ctx.guild.id)
command_channel_id = guild_config.command_channel_id
if command_channel_id:
command_channel = await ctx.bot.get_or_fetch_channel(
ctx.guild,
command_channel_id,
)
if ctx.channel.id != command_channel_id and command_channel:
raise LumiExceptions.NotAllowedInChannel(command_channel)
return True
return commands.check(predicate)

View file

@ -12,7 +12,7 @@ class Constants:
TITLE = "Luminara" TITLE = "Luminara"
AUTHOR = "wlinator" AUTHOR = "wlinator"
LICENSE = "GNU General Public License v3.0" LICENSE = "GNU General Public License v3.0"
VERSION = "2.8.4" # "Moderation: Config" update VERSION = "2.8.5" # "Moderation: Kicks" update
# bot credentials # bot credentials
TOKEN: Optional[str] = os.environ.get("TOKEN", None) TOKEN: Optional[str] = os.environ.get("TOKEN", None)

View file

@ -21,14 +21,18 @@ class EmbedBuilder:
image_url=None, image_url=None,
thumbnail_url=None, thumbnail_url=None,
timestamp=None, timestamp=None,
hide_author=False,
hide_author_icon=False,
): ):
if not author_text: if not hide_author:
author_text = ctx.author.name if not author_text:
elif show_name: author_text = ctx.author.name
description = f"**{ctx.author.name}** {description}" elif show_name:
description = f"**{ctx.author.name}** {description}"
if not hide_author_icon and not author_icon_url:
author_icon_url = ctx.author.display_avatar.url
if not author_icon_url:
author_icon_url = ctx.author.display_avatar.url
if not footer_text: if not footer_text:
footer_text = "Luminara" footer_text = "Luminara"
if not footer_icon_url: if not footer_icon_url:
@ -39,7 +43,12 @@ class EmbedBuilder:
description=description, description=description,
color=color or CONST.COLOR_DEFAULT, color=color or CONST.COLOR_DEFAULT,
) )
embed.set_author(name=author_text, icon_url=author_icon_url, url=author_url) if not hide_author:
embed.set_author(
name=author_text,
icon_url=None if hide_author_icon else author_icon_url,
url=author_url,
)
embed.set_footer(text=footer_text, icon_url=footer_icon_url) embed.set_footer(text=footer_text, icon_url=footer_icon_url)
embed.timestamp = timestamp or datetime.datetime.now() embed.timestamp = timestamp or datetime.datetime.now()
@ -63,6 +72,8 @@ class EmbedBuilder:
image_url=None, image_url=None,
thumbnail_url=None, thumbnail_url=None,
timestamp=None, timestamp=None,
hide_author=False,
hide_author_icon=False,
): ):
return EmbedBuilder.create_embed( return EmbedBuilder.create_embed(
ctx, ctx,
@ -78,6 +89,8 @@ class EmbedBuilder:
image_url=image_url, image_url=image_url,
thumbnail_url=thumbnail_url, thumbnail_url=thumbnail_url,
timestamp=timestamp, timestamp=timestamp,
hide_author=hide_author,
hide_author_icon=hide_author_icon,
) )
@staticmethod @staticmethod
@ -93,6 +106,8 @@ class EmbedBuilder:
image_url=None, image_url=None,
thumbnail_url=None, thumbnail_url=None,
timestamp=None, timestamp=None,
hide_author=False,
hide_author_icon=False,
): ):
return EmbedBuilder.create_embed( return EmbedBuilder.create_embed(
ctx, ctx,
@ -108,6 +123,8 @@ class EmbedBuilder:
image_url=image_url, image_url=image_url,
thumbnail_url=thumbnail_url, thumbnail_url=thumbnail_url,
timestamp=timestamp, timestamp=timestamp,
hide_author=hide_author,
hide_author_icon=hide_author_icon,
) )
@staticmethod @staticmethod
@ -123,6 +140,8 @@ class EmbedBuilder:
image_url=None, image_url=None,
thumbnail_url=None, thumbnail_url=None,
timestamp=None, timestamp=None,
hide_author=False,
hide_author_icon=False,
): ):
return EmbedBuilder.create_embed( return EmbedBuilder.create_embed(
ctx, ctx,
@ -138,6 +157,8 @@ class EmbedBuilder:
image_url=image_url, image_url=image_url,
thumbnail_url=thumbnail_url, thumbnail_url=thumbnail_url,
timestamp=timestamp, timestamp=timestamp,
hide_author=hide_author,
hide_author_icon=hide_author_icon,
) )
@staticmethod @staticmethod
@ -153,6 +174,8 @@ class EmbedBuilder:
image_url=None, image_url=None,
thumbnail_url=None, thumbnail_url=None,
timestamp=None, timestamp=None,
hide_author=False,
hide_author_icon=False,
): ):
return EmbedBuilder.create_embed( return EmbedBuilder.create_embed(
ctx, ctx,
@ -168,4 +191,6 @@ class EmbedBuilder:
image_url=image_url, image_url=image_url,
thumbnail_url=thumbnail_url, thumbnail_url=thumbnail_url,
timestamp=timestamp, timestamp=timestamp,
hide_author=hide_author,
hide_author_icon=hide_author_icon,
) )

View file

@ -1,6 +1,5 @@
import discord import discord
from lib import formatter
from lib.constants import CONST from lib.constants import CONST
@ -11,64 +10,6 @@ def clean_error_embed(ctx):
) )
class GenericErrors:
@staticmethod
def bad_arg(ctx, error):
embed = clean_error_embed(ctx)
if embed.description is None:
embed.description = formatter.shorten(str(error), 100)
else:
embed.description += formatter.shorten(str(error), 100)
embed.set_footer(
text=f"For more info do {formatter.get_prefix(ctx)}help {formatter.get_invoked_name(ctx)}",
icon_url=CONST.QUESTION_ICON,
)
return embed
@staticmethod
def private_message_only(ctx):
embed = clean_error_embed(ctx)
if embed.description is None:
embed.description = "this command can only be used in private messages."
else:
embed.description += "this command can only be used in private messages."
embed.set_footer(
text=f"For more info do '{formatter.get_prefix(ctx)}help {formatter.get_invoked_name(ctx)}'",
icon_url=CONST.QUESTION_ICON,
)
return embed
@staticmethod
def guild_only(ctx):
embed = clean_error_embed(ctx)
if embed.description is None:
embed.description = "this command can only be used in a server."
else:
embed.description += "this command can only be used in a server."
embed.set_footer(
text=f"For more info do '{formatter.get_prefix(ctx)}help {formatter.get_invoked_name(ctx)}'",
icon_url=CONST.QUESTION_ICON,
)
return embed
@staticmethod
def channel_not_allowed(ctx, channel):
embed = clean_error_embed(ctx)
if embed.description is None:
embed.description = f"you can only do that command in {channel.mention}."
else:
embed.description += f"you can only do that command in {channel.mention}."
embed.set_footer(
text="This message will delete itself after 5s",
icon_url=CONST.EXCLAIM_ICON,
)
return embed
class EconErrors: class EconErrors:
@staticmethod @staticmethod
def out_of_time(ctx): def out_of_time(ctx):
@ -98,35 +39,3 @@ class EconErrors:
) )
return embed return embed
class MiscErrors:
@staticmethod
def prefix_too_long(ctx):
embed = clean_error_embed(ctx)
if embed.description is None:
embed.description = "this prefix is too long."
else:
embed.description += "this prefix is too long."
embed.set_footer(
text=f"For more info do '{formatter.get_prefix(ctx)}help {formatter.get_invoked_name(ctx)}'",
icon_url=CONST.QUESTION_ICON,
)
return embed
class HelpErrors:
@staticmethod
def error_message(ctx, error):
"""
See discord.ext.commands.HelpCommand.send_error_message
"""
embed = clean_error_embed(ctx)
embed.description += error
embed.set_footer(
text=f"See '{formatter.get_prefix(ctx)}help'",
icon_url=CONST.EXCLAIM_ICON,
)
return embed

View file

@ -1,15 +1,6 @@
from discord.ext import commands from discord.ext import commands
class NotAllowedInChannel(commands.CheckFailure):
"""
Raised when checks.allowed_in_channel() fails.
"""
def __init__(self, commands_channel):
self.command_channel = commands_channel
class BirthdaysDisabled(commands.CheckFailure): class BirthdaysDisabled(commands.CheckFailure):
""" """
Raised when the birthdays module is disabled in ctx.guild. Raised when the birthdays module is disabled in ctx.guild.

View file

@ -1,7 +1,5 @@
import discord import discord
from discord.ext import bridge, commands from discord.ext import bridge, commands
from lib import checks
from modules.economy import balance, blackjack, daily, give, slots from modules.economy import balance, blackjack, daily, give, slots
@ -18,7 +16,6 @@ class Economy(commands.Cog):
guild_only=True, guild_only=True,
) )
@commands.guild_only() @commands.guild_only()
@checks.allowed_in_channel()
@commands.cooldown(1, 10, commands.BucketType.user) @commands.cooldown(1, 10, commands.BucketType.user)
async def balance_command(self, ctx): async def balance_command(self, ctx):
return await balance.cmd(ctx) return await balance.cmd(ctx)
@ -31,7 +28,6 @@ class Economy(commands.Cog):
guild_only=True, guild_only=True,
) )
@commands.guild_only() @commands.guild_only()
@checks.allowed_in_channel()
async def blackjack_command(self, ctx, *, bet: int): async def blackjack_command(self, ctx, *, bet: int):
return await blackjack.cmd(ctx, bet) return await blackjack.cmd(ctx, bet)
@ -43,7 +39,6 @@ class Economy(commands.Cog):
guild_only=True, guild_only=True,
) )
@commands.guild_only() @commands.guild_only()
@checks.allowed_in_channel()
async def daily_command(self, ctx): async def daily_command(self, ctx):
return await daily.cmd(ctx) return await daily.cmd(ctx)
@ -53,7 +48,6 @@ class Economy(commands.Cog):
guild_only=True, guild_only=True,
) )
@commands.guild_only() @commands.guild_only()
@checks.allowed_in_channel()
async def give_command(self, ctx, *, user: discord.Member, amount: int): async def give_command(self, ctx, *, user: discord.Member, amount: int):
return await give.cmd(ctx, user, amount) return await give.cmd(ctx, user, amount)
@ -62,7 +56,6 @@ class Economy(commands.Cog):
help="Give a server member some cash. You can use ID or mention them.", help="Give a server member some cash. You can use ID or mention them.",
) )
@commands.guild_only() @commands.guild_only()
@checks.allowed_in_channel()
async def give_command_prefixed(self, ctx, user: discord.User, *, amount: int): async def give_command_prefixed(self, ctx, user: discord.User, *, amount: int):
try: try:
member = await ctx.guild.fetch_member(user.id) member = await ctx.guild.fetch_member(user.id)
@ -79,7 +72,6 @@ class Economy(commands.Cog):
guild_only=True, guild_only=True,
) )
@commands.guild_only() @commands.guild_only()
@checks.allowed_in_channel()
@commands.cooldown(1, 5, commands.BucketType.user) @commands.cooldown(1, 5, commands.BucketType.user)
async def slots_command(self, ctx, *, bet: int): async def slots_command(self, ctx, *, bet: int):
return await slots.cmd(self, ctx, bet) return await slots.cmd(self, ctx, bet)

View file

@ -8,7 +8,7 @@ from loguru import logger
from config.parser import JsonCache from config.parser import JsonCache
from lib import interaction from lib import interaction
from lib.embeds.error import EconErrors, GenericErrors from lib.embeds.error import EconErrors
from services.currency_service import Currency from services.currency_service import Currency
from services.stats_service import BlackJackStats from services.stats_service import BlackJackStats
@ -187,7 +187,7 @@ async def cmd(ctx, bet: int):
stats.push() stats.push()
except Exception as e: except Exception as e:
await ctx.respond(embed=GenericErrors.default_exception(ctx)) # await ctx.respond(embed=GenericErrors.default_exception(ctx))
logger.error("Something went wrong in the blackjack command: ", e) logger.error("Something went wrong in the blackjack command: ", e)
finally: finally:

View file

@ -1,6 +1,5 @@
from discord.ext import bridge, commands from discord.ext import bridge, commands
from lib import checks
from modules.levels import leaderboard, level from modules.levels import leaderboard, level
@ -16,7 +15,6 @@ class Levels(commands.Cog):
guild_only=True, guild_only=True,
) )
@commands.guild_only() @commands.guild_only()
@checks.allowed_in_channel()
@commands.cooldown(1, 30, commands.BucketType.user) @commands.cooldown(1, 30, commands.BucketType.user)
async def level_command(self, ctx) -> None: async def level_command(self, ctx) -> None:
await level.rank(ctx) await level.rank(ctx)
@ -29,7 +27,6 @@ class Levels(commands.Cog):
guild_only=True, guild_only=True,
) )
@commands.guild_only() @commands.guild_only()
@checks.allowed_in_channel()
@commands.cooldown(1, 180, commands.BucketType.user) @commands.cooldown(1, 180, commands.BucketType.user)
async def leaderboard_command(self, ctx) -> None: async def leaderboard_command(self, ctx) -> None:
await leaderboard.cmd(ctx) await leaderboard.cmd(ctx)

View file

@ -5,7 +5,6 @@ from discord.commands import SlashCommandGroup
from discord.ext import bridge, commands, tasks from discord.ext import bridge, commands, tasks
from Client import LumiBot from Client import LumiBot
from lib import checks
from modules.config import c_prefix from modules.config import c_prefix
from modules.misc import avatar, backup, info, introduction, invite, ping, xkcd from modules.misc import avatar, backup, info, introduction, invite, ping, xkcd
@ -28,7 +27,6 @@ class Misc(commands.Cog):
guild_only=True, guild_only=True,
) )
@commands.guild_only() @commands.guild_only()
@checks.allowed_in_channel()
async def avatar(self, ctx, user: discord.Member) -> None: async def avatar(self, ctx, user: discord.Member) -> None:
return await avatar.get_avatar(ctx, user) return await avatar.get_avatar(ctx, user)
@ -38,7 +36,6 @@ class Misc(commands.Cog):
description="Simple status check.", description="Simple status check.",
help="Simple status check.", help="Simple status check.",
) )
@checks.allowed_in_channel()
async def ping(self, ctx) -> None: async def ping(self, ctx) -> None:
await ping.ping(self, ctx) await ping.ping(self, ctx)
@ -47,7 +44,6 @@ class Misc(commands.Cog):
description="See Lumi's uptime since the last update.", description="See Lumi's uptime since the last update.",
help="See how long Lumi has been online since his last update.", help="See how long Lumi has been online since his last update.",
) )
@checks.allowed_in_channel()
async def uptime(self, ctx) -> None: async def uptime(self, ctx) -> None:
await ping.uptime(self, ctx, self.start_time) await ping.uptime(self, ctx, self.start_time)
@ -56,7 +52,6 @@ class Misc(commands.Cog):
description="Generate an invite link.", description="Generate an invite link.",
help="Generate a link to invite Lumi to your own server!", help="Generate a link to invite Lumi to your own server!",
) )
@checks.allowed_in_channel()
async def invite_command(self, ctx) -> None: async def invite_command(self, ctx) -> None:
await invite.cmd(ctx) await invite.cmd(ctx)
@ -67,7 +62,6 @@ class Misc(commands.Cog):
guild_only=True, guild_only=True,
) )
@commands.guild_only() @commands.guild_only()
@checks.allowed_in_channel()
async def prefix_command(self, ctx) -> None: async def prefix_command(self, ctx) -> None:
await c_prefix.get_prefix(ctx) await c_prefix.get_prefix(ctx)
@ -77,7 +71,6 @@ class Misc(commands.Cog):
description="Shows basic Lumi stats.", description="Shows basic Lumi stats.",
help="Shows basic Lumi stats.", help="Shows basic Lumi stats.",
) )
@checks.allowed_in_channel()
async def info_command(self, ctx) -> None: async def info_command(self, ctx) -> None:
unix_timestamp: int = int(round(self.start_time.timestamp())) unix_timestamp: int = int(round(self.start_time.timestamp()))
await info.cmd(self, ctx, unix_timestamp) await info.cmd(self, ctx, unix_timestamp)

View file

@ -1,7 +1,7 @@
import discord import discord
from discord.ext import bridge, commands from discord.ext import bridge, commands
from modules.moderation import ban, cases, warn, timeout from modules.moderation import ban, cases, warn, timeout, kick
class Moderation(commands.Cog): class Moderation(commands.Cog):
@ -163,6 +163,25 @@ class Moderation(commands.Cog):
): ):
await timeout.untimeout_user(ctx, target, reason) await timeout.untimeout_user(ctx, target, reason)
@bridge.bridge_command(
name="kick",
aliases=["k"],
description="Kick a user from the server.",
help="Kicks a user from the server.",
guild_only=True,
)
@bridge.has_permissions(kick_members=True)
@commands.bot_has_permissions(kick_members=True)
@commands.guild_only()
async def kick_command(
self,
ctx,
target: discord.Member,
*,
reason: str | None = None,
):
await kick.kick_user(self, ctx, target, reason)
def setup(client): def setup(client):
client.add_cog(Moderation(client)) client.add_cog(Moderation(client))

View file

@ -0,0 +1,57 @@
import asyncio
import discord
from lib import formatter
from lib.constants import CONST
from lib.embed_builder import EmbedBuilder
from modules.moderation.utils.actionable import async_actionable
from modules.moderation.utils.case_handler import create_case
from typing import Optional
from discord.ext.commands import UserConverter
async def kick_user(cog, ctx, target: discord.Member, reason: Optional[str] = None):
bot_member = await cog.client.get_or_fetch_member(ctx.guild, ctx.bot.user.id)
await async_actionable(target, ctx.author, bot_member)
output_reason = reason or CONST.STRINGS["mod_no_reason"]
try:
await target.send(
embed=EmbedBuilder.create_warning_embed(
ctx,
author_text=CONST.STRINGS["mod_kicked_author"],
description=CONST.STRINGS["mod_kick_dm"].format(
target.name,
ctx.guild.name,
output_reason,
),
show_name=False,
),
)
dm_sent = True
except (discord.HTTPException, discord.Forbidden):
dm_sent = False
await target.kick(
reason=CONST.STRINGS["mod_reason"].format(
ctx.author.name,
formatter.shorten(output_reason, 200),
),
)
respond_task = ctx.respond(
embed=EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["mod_kicked_author"],
description=CONST.STRINGS["mod_kicked_user"].format(target.name),
footer_text=CONST.STRINGS["mod_dm_sent"]
if dm_sent
else CONST.STRINGS["mod_dm_not_sent"],
),
)
target_user = await UserConverter().convert(ctx, str(target.id))
create_case_task = create_case(ctx, target_user, "KICK", reason)
await asyncio.gather(respond_task, create_case_task, return_exceptions=True)

View file

@ -1,10 +1,9 @@
import discord import discord
from discord.ext import commands from discord.ext import commands
from config.parser import JsonCache from lib.embed_builder import EmbedBuilder
from lib.embeds.error import HelpErrors from lib.constants import CONST
from lib.exceptions.LumiExceptions import LumiException
art = JsonCache.read_json("art")
class LumiHelp(commands.HelpCommand): class LumiHelp(commands.HelpCommand):
@ -22,10 +21,11 @@ class LumiHelp(commands.HelpCommand):
return "`{}{}`".format(self.context.clean_prefix, command.qualified_name) return "`{}{}`".format(self.context.clean_prefix, command.qualified_name)
async def send_bot_help(self, mapping): async def send_bot_help(self, mapping):
embed = discord.Embed(color=discord.Color.blurple()) embed = EmbedBuilder.create_info_embed(
ctx=self.context,
embed.set_author(name="Help Command", icon_url=art["logo"]["transparent"]) author_text="Help Command",
embed.description = "Full list of commands: https://wiki.wlinator.org/cmdlist" show_name=False,
)
for cog, lumi_commands in mapping.items(): for cog, lumi_commands in mapping.items():
filtered = await self.filter_commands(lumi_commands, sort=True) filtered = await self.filter_commands(lumi_commands, sort=True)
@ -46,10 +46,11 @@ class LumiHelp(commands.HelpCommand):
await channel.send(embed=embed) await channel.send(embed=embed)
async def send_command_help(self, command): async def send_command_help(self, command):
embed = discord.Embed( embed = EmbedBuilder.create_success_embed(
title=f"{self.context.clean_prefix}{command.qualified_name}", ctx=self.context,
color=discord.Color.blurple(), author_text=f"{self.context.clean_prefix}{command.qualified_name}",
description=command.help, description=command.help,
show_name=False,
) )
usage_value = "`{}{} {}`".format( usage_value = "`{}{} {}`".format(
@ -69,25 +70,16 @@ class LumiHelp(commands.HelpCommand):
await channel.send(embed=embed) await channel.send(embed=embed)
async def send_error_message(self, error): async def send_error_message(self, error):
channel = self.get_destination() raise LumiException(error)
await channel.send(embed=HelpErrors.error_message(self.context, error))
async def send_group_help(self, group): async def send_group_help(self, group):
channel = self.get_destination() raise LumiException(
await channel.send( CONST.STRINGS["error_command_not_found"].format(group.qualified_name),
embed=HelpErrors.error_message(
self.context,
f'No command called "{group.qualified_name}" found.',
),
) )
async def send_cog_help(self, cog): async def send_cog_help(self, cog):
channel = self.get_destination() raise LumiException(
await channel.send( CONST.STRINGS["error_command_not_found"].format(cog.qualified_name),
embed=HelpErrors.error_message(
self.context,
f'No command called "{cog.qualified_name}" found.',
),
) )
async def command_callback(self, ctx, *, command=None): async def command_callback(self, ctx, *, command=None):
@ -103,7 +95,7 @@ class LumiHelp(commands.HelpCommand):
if cog is not None: if cog is not None:
return await self.send_cog_help(cog) return await self.send_cog_help(cog)
maybe_coro = discord.utils.maybe_coroutine maybe_coro = discord.utils.maybe_coroutine # type: ignore
# If it's not a cog then it's a command. # If it's not a cog then it's a command.
# Since we want to have detailed errors when someone # Since we want to have detailed errors when someone