mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-08 12:18:54 -05:00
Merge remote-tracking branch 'release/V3/develop' into V3/develop
This commit is contained in:
commit
b83c7bf453
@ -169,7 +169,7 @@ Trivia
|
|||||||
Utility Functions
|
Utility Functions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
* New: ``chat_formatting.humaize_timedelta`` (`#2412`_)
|
* New: ``chat_formatting.humanize_timedelta`` (`#2412`_)
|
||||||
* ``Tunnel`` - Spelling correction of method name - changed ``files_from_attatch`` to ``files_from_attach`` (old name is left for backwards compatibility) (`#2496`_)
|
* ``Tunnel`` - Spelling correction of method name - changed ``files_from_attatch`` to ``files_from_attach`` (old name is left for backwards compatibility) (`#2496`_)
|
||||||
* ``Tunnel`` - fixed behavior of ``react_close()``, now when tunnel closes message will be sent to other end (`#2507`_)
|
* ``Tunnel`` - fixed behavior of ``react_close()``, now when tunnel closes message will be sent to other end (`#2507`_)
|
||||||
* ``chat_formatting.humanize_list`` - Improved error handling of empty lists (`#2597`_)
|
* ``chat_formatting.humanize_list`` - Improved error handling of empty lists (`#2597`_)
|
||||||
|
|||||||
@ -188,23 +188,17 @@ Choose one of the following commands to install Red.
|
|||||||
If you're not inside an activated virtual environment, include the ``--user`` flag with all
|
If you're not inside an activated virtual environment, include the ``--user`` flag with all
|
||||||
``python3.7 -m pip`` commands.
|
``python3.7 -m pip`` commands.
|
||||||
|
|
||||||
To install without audio support:
|
To install without MongoDB support:
|
||||||
|
|
||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
|
|
||||||
python3.7 -m pip install -U Red-DiscordBot
|
python3.7 -m pip install -U Red-DiscordBot
|
||||||
|
|
||||||
Or, to install with audio support:
|
Or, to install with MongoDB support:
|
||||||
|
|
||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
|
|
||||||
python3.7 -m pip install -U Red-DiscordBot[voice]
|
python3.7 -m pip install -U Red-DiscordBot[mongo]
|
||||||
|
|
||||||
Or, install with audio and MongoDB support:
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
python3.7 -m pip install -U Red-DiscordBot[voice,mongo]
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|||||||
@ -62,23 +62,17 @@ Installing Red
|
|||||||
If you're not inside an activated virtual environment, include the ``--user`` flag with all
|
If you're not inside an activated virtual environment, include the ``--user`` flag with all
|
||||||
``pip`` commands.
|
``pip`` commands.
|
||||||
|
|
||||||
* No audio:
|
* No MongoDB support:
|
||||||
|
|
||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
|
|
||||||
python -m pip install -U Red-DiscordBot
|
python -m pip install -U Red-DiscordBot
|
||||||
|
|
||||||
* With audio:
|
* With MongoDB support:
|
||||||
|
|
||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
|
|
||||||
python -m pip install -U Red-DiscordBot[voice]
|
python -m pip install -U Red-DiscordBot[mongo]
|
||||||
|
|
||||||
* With audio and MongoDB support:
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
python -m pip install -U Red-DiscordBot[voice,mongo]
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|||||||
@ -174,7 +174,7 @@ class VersionInfo:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
__version__ = "3.1.0"
|
__version__ = "3.1.1"
|
||||||
version_info = VersionInfo.from_str(__version__)
|
version_info = VersionInfo.from_str(__version__)
|
||||||
|
|
||||||
# Filter fuzzywuzzy slow sequence matcher warning
|
# Filter fuzzywuzzy slow sequence matcher warning
|
||||||
|
|||||||
@ -199,6 +199,7 @@ class Downloader(commands.Cog):
|
|||||||
if not deps:
|
if not deps:
|
||||||
return await ctx.send_help()
|
return await ctx.send_help()
|
||||||
repo = Repo("", "", "", Path.cwd(), loop=ctx.bot.loop)
|
repo = Repo("", "", "", Path.cwd(), loop=ctx.bot.loop)
|
||||||
|
async with ctx.typing():
|
||||||
success = await repo.install_raw_requirements(deps, self.LIB_PATH)
|
success = await repo.install_raw_requirements(deps, self.LIB_PATH)
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
|
|||||||
@ -114,6 +114,8 @@ class Installable(RepoJSONMixin):
|
|||||||
if self._location.is_file():
|
if self._location.is_file():
|
||||||
copy_func = shutil.copy2
|
copy_func = shutil.copy2
|
||||||
else:
|
else:
|
||||||
|
# clear copy_tree's cache to make sure missing directories are created (GH-2690)
|
||||||
|
distutils.dir_util._path_created = {}
|
||||||
copy_func = distutils.dir_util.copy_tree
|
copy_func = distutils.dir_util.copy_tree
|
||||||
|
|
||||||
# noinspection PyBroadException
|
# noinspection PyBroadException
|
||||||
|
|||||||
@ -307,14 +307,17 @@ class General(commands.Cog):
|
|||||||
messages = []
|
messages = []
|
||||||
for ud in data["list"]:
|
for ud in data["list"]:
|
||||||
ud.setdefault("example", "N/A")
|
ud.setdefault("example", "N/A")
|
||||||
description = _("{definition}\n\n**Example:** {example}").format(**ud)
|
|
||||||
if len(description) > 2048:
|
|
||||||
description = "{}...".format(description[:2045])
|
|
||||||
|
|
||||||
message = _(
|
message = _(
|
||||||
"<{permalink}>\n {word} by {author}\n\n{description}\n\n"
|
"<{permalink}>\n {word} by {author}\n\n{description}\n\n"
|
||||||
"{thumbs_down} Down / {thumbs_up} Up, Powered by Urban Dictionary."
|
"{thumbs_down} Down / {thumbs_up} Up, Powered by Urban Dictionary."
|
||||||
).format(word=ud.pop("word").capitalize(), description=description, **ud)
|
).format(word=ud.pop("word").capitalize(), description="{description}", **ud)
|
||||||
|
max_desc_len = 2000 - len(message)
|
||||||
|
|
||||||
|
description = _("{definition}\n\n**Example:** {example}").format(**ud)
|
||||||
|
if len(description) > max_desc_len:
|
||||||
|
description = "{}...".format(description[: max_desc_len - 3])
|
||||||
|
|
||||||
|
message = message.format(description=description)
|
||||||
messages.append(message)
|
messages.append(message)
|
||||||
|
|
||||||
if messages is not None and len(messages) > 0:
|
if messages is not None and len(messages) > 0:
|
||||||
|
|||||||
@ -145,7 +145,7 @@ class ModInfo(MixinMeta):
|
|||||||
if voice_state and voice_state.channel:
|
if voice_state and voice_state.channel:
|
||||||
data.add_field(
|
data.add_field(
|
||||||
name=_("Current voice channel"),
|
name=_("Current voice channel"),
|
||||||
value="{0.name} (ID {0.id})".format(voice_state.channel),
|
value="{0.mention} ID: {0.id}".format(voice_state.channel),
|
||||||
inline=False,
|
inline=False,
|
||||||
)
|
)
|
||||||
data.set_footer(text=_("Member #{} | User ID: {}").format(member_number, user.id))
|
data.set_footer(text=_("Member #{} | User ID: {}").format(member_number, user.id))
|
||||||
@ -164,7 +164,7 @@ class ModInfo(MixinMeta):
|
|||||||
await ctx.send(embed=data)
|
await ctx.send(embed=data)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def names(self, ctx: commands.Context, user: discord.Member):
|
async def names(self, ctx: commands.Context, *, user: discord.Member):
|
||||||
"""Show previous names and nicknames of a user."""
|
"""Show previous names and nicknames of a user."""
|
||||||
names, nicks = await self.get_names_and_nicks(user)
|
names, nicks = await self.get_names_and_nicks(user)
|
||||||
msg = ""
|
msg = ""
|
||||||
|
|||||||
@ -128,10 +128,9 @@ class Streams(commands.Cog):
|
|||||||
stream = PicartoStream(name=channel_name)
|
stream = PicartoStream(name=channel_name)
|
||||||
await self.check_online(ctx, stream)
|
await self.check_online(ctx, stream)
|
||||||
|
|
||||||
@staticmethod
|
async def check_online(self, ctx: commands.Context, stream):
|
||||||
async def check_online(ctx: commands.Context, stream):
|
|
||||||
try:
|
try:
|
||||||
embed = await stream.is_online()
|
info = await stream.is_online()
|
||||||
except OfflineStream:
|
except OfflineStream:
|
||||||
await ctx.send(_("That user is offline."))
|
await ctx.send(_("That user is offline."))
|
||||||
except StreamNotFound:
|
except StreamNotFound:
|
||||||
@ -155,6 +154,14 @@ class Streams(commands.Cog):
|
|||||||
_("Something went wrong whilst trying to contact the stream service's API.")
|
_("Something went wrong whilst trying to contact the stream service's API.")
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
if isinstance(info, tuple):
|
||||||
|
embed, is_rerun = info
|
||||||
|
ignore_reruns = await self.db.guild(ctx.channel.guild).ignore_reruns()
|
||||||
|
if ignore_reruns and is_rerun:
|
||||||
|
await ctx.send(_("That user is offline."))
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
embed = info
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@commands.group()
|
@commands.group()
|
||||||
|
|||||||
@ -57,7 +57,7 @@ class Trivia(commands.Cog):
|
|||||||
settings_dict = await settings.all()
|
settings_dict = await settings.all()
|
||||||
msg = box(
|
msg = box(
|
||||||
_(
|
_(
|
||||||
"**Current settings**\n"
|
"Current settings\n"
|
||||||
"Bot gains points: {bot_plays}\n"
|
"Bot gains points: {bot_plays}\n"
|
||||||
"Answer time limit: {delay} seconds\n"
|
"Answer time limit: {delay} seconds\n"
|
||||||
"Lack of response timeout: {timeout} seconds\n"
|
"Lack of response timeout: {timeout} seconds\n"
|
||||||
|
|||||||
@ -52,6 +52,10 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin):
|
|||||||
custom_info=None,
|
custom_info=None,
|
||||||
help__page_char_limit=1000,
|
help__page_char_limit=1000,
|
||||||
help__max_pages_in_guild=2,
|
help__max_pages_in_guild=2,
|
||||||
|
help__use_menus=False,
|
||||||
|
help__show_hidden=False,
|
||||||
|
help__verify_checks=True,
|
||||||
|
help__verify_exists=False,
|
||||||
help__tagline="",
|
help__tagline="",
|
||||||
disabled_commands=[],
|
disabled_commands=[],
|
||||||
disabled_command_msg="That command is disabled.",
|
disabled_command_msg="That command is disabled.",
|
||||||
|
|||||||
@ -77,14 +77,6 @@ class RedHelpFormatter:
|
|||||||
should not need or want a shared state.
|
should not need or want a shared state.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Class vars for things which should be configurable at a later date but aren't now
|
|
||||||
# Technically, someone can just use a cog to switch these in real time for now.
|
|
||||||
|
|
||||||
USE_MENU = False
|
|
||||||
CONFIRM_UNAVAILABLE_COMMAND_EXISTENCES = False
|
|
||||||
SHOW_HIDDEN = False
|
|
||||||
VERIFY_CHECKS = True
|
|
||||||
|
|
||||||
async def send_help(self, ctx: Context, help_for: HelpTarget = None):
|
async def send_help(self, ctx: Context, help_for: HelpTarget = None):
|
||||||
"""
|
"""
|
||||||
This delegates to other functions.
|
This delegates to other functions.
|
||||||
@ -102,7 +94,7 @@ class RedHelpFormatter:
|
|||||||
await self.command_not_found(ctx, help_for)
|
await self.command_not_found(ctx, help_for)
|
||||||
return
|
return
|
||||||
except NoSubCommand as exc:
|
except NoSubCommand as exc:
|
||||||
if self.CONFIRM_UNAVAILABLE_COMMAND_EXISTENCES:
|
if await ctx.bot.db.help.verify_exists():
|
||||||
await self.subcommand_not_found(ctx, exc.last, exc.not_found)
|
await self.subcommand_not_found(ctx, exc.last, exc.not_found)
|
||||||
return
|
return
|
||||||
help_for = exc.last
|
help_for = exc.last
|
||||||
@ -138,7 +130,7 @@ class RedHelpFormatter:
|
|||||||
|
|
||||||
async def format_command_help(self, ctx: Context, obj: commands.Command):
|
async def format_command_help(self, ctx: Context, obj: commands.Command):
|
||||||
|
|
||||||
send = self.CONFIRM_UNAVAILABLE_COMMAND_EXISTENCES
|
send = await ctx.bot.db.help.verify_exists()
|
||||||
if not send:
|
if not send:
|
||||||
async for _ in self.help_filter_func(ctx, (obj,), bypass_hidden=True):
|
async for _ in self.help_filter_func(ctx, (obj,), bypass_hidden=True):
|
||||||
# This is a really lazy option for not
|
# This is a really lazy option for not
|
||||||
@ -182,8 +174,14 @@ class RedHelpFormatter:
|
|||||||
emb["fields"].append(field)
|
emb["fields"].append(field)
|
||||||
|
|
||||||
if subcommands:
|
if subcommands:
|
||||||
|
|
||||||
|
def shorten_line(a_line: str) -> str:
|
||||||
|
if len(a_line) < 70: # embed max width needs to be lower
|
||||||
|
return a_line
|
||||||
|
return a_line[:67] + "..."
|
||||||
|
|
||||||
subtext = "\n".join(
|
subtext = "\n".join(
|
||||||
f"**{name}** {command.short_doc}"
|
shorten_line(f"**{name}** {command.short_doc}")
|
||||||
for name, command in sorted(subcommands.items())
|
for name, command in sorted(subcommands.items())
|
||||||
)
|
)
|
||||||
for i, page in enumerate(pagify(subtext, page_length=1000, shorten_by=0)):
|
for i, page in enumerate(pagify(subtext, page_length=1000, shorten_by=0)):
|
||||||
@ -208,7 +206,7 @@ class RedHelpFormatter:
|
|||||||
doc_max_width = 80 - max_width
|
doc_max_width = 80 - max_width
|
||||||
for nm, com in sorted(cmds):
|
for nm, com in sorted(cmds):
|
||||||
width_gap = discord.utils._string_width(nm) - len(nm)
|
width_gap = discord.utils._string_width(nm) - len(nm)
|
||||||
doc = command.short_doc
|
doc = com.short_doc
|
||||||
if len(doc) > doc_max_width:
|
if len(doc) > doc_max_width:
|
||||||
doc = doc[: doc_max_width - 3] + "..."
|
doc = doc[: doc_max_width - 3] + "..."
|
||||||
yield nm, doc, max_width - width_gap
|
yield nm, doc, max_width - width_gap
|
||||||
@ -251,6 +249,12 @@ class RedHelpFormatter:
|
|||||||
|
|
||||||
author_info = {"name": f"{ctx.me.display_name} Help Menu", "icon_url": ctx.me.avatar_url}
|
author_info = {"name": f"{ctx.me.display_name} Help Menu", "icon_url": ctx.me.avatar_url}
|
||||||
|
|
||||||
|
if not field_groups: # This can happen on single command without a docstring
|
||||||
|
embed = discord.Embed(color=color, **embed_dict["embed"])
|
||||||
|
embed.set_author(**author_info)
|
||||||
|
embed.set_footer(**embed_dict["footer"])
|
||||||
|
pages.append(embed)
|
||||||
|
|
||||||
for i, group in enumerate(field_groups, 1):
|
for i, group in enumerate(field_groups, 1):
|
||||||
embed = discord.Embed(color=color, **embed_dict["embed"])
|
embed = discord.Embed(color=color, **embed_dict["embed"])
|
||||||
|
|
||||||
@ -271,8 +275,8 @@ class RedHelpFormatter:
|
|||||||
|
|
||||||
async def format_cog_help(self, ctx: Context, obj: commands.Cog):
|
async def format_cog_help(self, ctx: Context, obj: commands.Cog):
|
||||||
|
|
||||||
commands = await self.get_cog_help_mapping(ctx, obj)
|
coms = await self.get_cog_help_mapping(ctx, obj)
|
||||||
if not (commands or self.CONFIRM_UNAVAILABLE_COMMAND_EXISTENCES):
|
if not (coms or await ctx.bot.db.help.verify_exists()):
|
||||||
return
|
return
|
||||||
|
|
||||||
description = obj.help
|
description = obj.help
|
||||||
@ -285,9 +289,16 @@ class RedHelpFormatter:
|
|||||||
if description:
|
if description:
|
||||||
emb["embed"]["title"] = f"*{description[:2044]}*"
|
emb["embed"]["title"] = f"*{description[:2044]}*"
|
||||||
|
|
||||||
if commands:
|
if coms:
|
||||||
|
|
||||||
|
def shorten_line(a_line: str) -> str:
|
||||||
|
if len(a_line) < 70: # embed max width needs to be lower
|
||||||
|
return a_line
|
||||||
|
return a_line[:67] + "..."
|
||||||
|
|
||||||
command_text = "\n".join(
|
command_text = "\n".join(
|
||||||
f"**{name}** {command.short_doc}" for name, command in sorted(commands.items())
|
shorten_line(f"**{name}** {command.short_doc}")
|
||||||
|
for name, command in sorted(coms.items())
|
||||||
)
|
)
|
||||||
for i, page in enumerate(pagify(command_text, page_length=1000, shorten_by=0)):
|
for i, page in enumerate(pagify(command_text, page_length=1000, shorten_by=0)):
|
||||||
if i == 0:
|
if i == 0:
|
||||||
@ -300,11 +311,11 @@ class RedHelpFormatter:
|
|||||||
await self.make_and_send_embeds(ctx, emb)
|
await self.make_and_send_embeds(ctx, emb)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
commands_text = None
|
subtext = None
|
||||||
commands_header = None
|
subtext_header = None
|
||||||
if commands:
|
if coms:
|
||||||
subtext_header = "Commands:"
|
subtext_header = "Commands:"
|
||||||
max_width = max(discord.utils._string_width(name) for name in commands.keys())
|
max_width = max(discord.utils._string_width(name) for name in coms.keys())
|
||||||
|
|
||||||
def width_maker(cmds):
|
def width_maker(cmds):
|
||||||
doc_max_width = 80 - max_width
|
doc_max_width = 80 - max_width
|
||||||
@ -316,20 +327,17 @@ class RedHelpFormatter:
|
|||||||
yield nm, doc, max_width - width_gap
|
yield nm, doc, max_width - width_gap
|
||||||
|
|
||||||
subtext = "\n".join(
|
subtext = "\n".join(
|
||||||
f" {name:<{width}} {doc}"
|
f" {name:<{width}} {doc}" for name, doc, width in width_maker(coms.items())
|
||||||
for name, doc, width in width_maker(commands.items())
|
|
||||||
)
|
)
|
||||||
|
|
||||||
to_page = "\n\n".join(
|
to_page = "\n\n".join(filter(None, (description, subtext_header, subtext)))
|
||||||
filter(None, (description, signature[1:-1], subtext_header, subtext))
|
|
||||||
)
|
|
||||||
pages = [box(p) for p in pagify(to_page)]
|
pages = [box(p) for p in pagify(to_page)]
|
||||||
await self.send_pages(ctx, pages, embed=False)
|
await self.send_pages(ctx, pages, embed=False)
|
||||||
|
|
||||||
async def format_bot_help(self, ctx: Context):
|
async def format_bot_help(self, ctx: Context):
|
||||||
|
|
||||||
commands = await self.get_bot_help_mapping(ctx)
|
coms = await self.get_bot_help_mapping(ctx)
|
||||||
if not commands:
|
if not coms:
|
||||||
return
|
return
|
||||||
|
|
||||||
description = ctx.bot.description or ""
|
description = ctx.bot.description or ""
|
||||||
@ -343,15 +351,21 @@ class RedHelpFormatter:
|
|||||||
if description:
|
if description:
|
||||||
emb["embed"]["title"] = f"*{description[:2044]}*"
|
emb["embed"]["title"] = f"*{description[:2044]}*"
|
||||||
|
|
||||||
for cog_name, data in commands:
|
for cog_name, data in coms:
|
||||||
|
|
||||||
if cog_name:
|
if cog_name:
|
||||||
title = f"**__{cog_name}:__**"
|
title = f"**__{cog_name}:__**"
|
||||||
else:
|
else:
|
||||||
title = f"**__No Category:__**"
|
title = f"**__No Category:__**"
|
||||||
|
|
||||||
|
def shorten_line(a_line: str) -> str:
|
||||||
|
if len(a_line) < 70: # embed max width needs to be lower
|
||||||
|
return a_line
|
||||||
|
return a_line[:67] + "..."
|
||||||
|
|
||||||
cog_text = "\n".join(
|
cog_text = "\n".join(
|
||||||
f"**{name}** {command.short_doc}" for name, command in sorted(data.items())
|
shorten_line(f"**{name}** {command.short_doc}")
|
||||||
|
for name, command in sorted(data.items())
|
||||||
)
|
)
|
||||||
|
|
||||||
for i, page in enumerate(pagify(cog_text, page_length=1000, shorten_by=0)):
|
for i, page in enumerate(pagify(cog_text, page_length=1000, shorten_by=0)):
|
||||||
@ -362,11 +376,12 @@ class RedHelpFormatter:
|
|||||||
await self.make_and_send_embeds(ctx, emb)
|
await self.make_and_send_embeds(ctx, emb)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
to_join = []
|
||||||
if description:
|
if description:
|
||||||
to_join = [f"{description}\n"]
|
to_join.append(f"{description}\n")
|
||||||
|
|
||||||
names = []
|
names = []
|
||||||
for k, v in commands:
|
for k, v in coms:
|
||||||
names.extend(list(v.name for v in v.values()))
|
names.extend(list(v.name for v in v.values()))
|
||||||
|
|
||||||
max_width = max(
|
max_width = max(
|
||||||
@ -382,7 +397,7 @@ class RedHelpFormatter:
|
|||||||
doc = doc[: doc_max_width - 3] + "..."
|
doc = doc[: doc_max_width - 3] + "..."
|
||||||
yield nm, doc, max_width - width_gap
|
yield nm, doc, max_width - width_gap
|
||||||
|
|
||||||
for cog_name, data in commands:
|
for cog_name, data in coms:
|
||||||
|
|
||||||
title = f"{cog_name}:" if cog_name else "No Category:"
|
title = f"{cog_name}:" if cog_name else "No Category:"
|
||||||
to_join.append(title)
|
to_join.append(title)
|
||||||
@ -401,17 +416,25 @@ class RedHelpFormatter:
|
|||||||
"""
|
"""
|
||||||
This does most of actual filtering.
|
This does most of actual filtering.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
show_hidden = bypass_hidden or await ctx.bot.db.help.show_hidden()
|
||||||
|
verify_checks = await ctx.bot.db.help.verify_checks()
|
||||||
|
|
||||||
# TODO: Settings for this in core bot db
|
# TODO: Settings for this in core bot db
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
if self.VERIFY_CHECKS and not (self.SHOW_HIDDEN or bypass_hidden):
|
if verify_checks and not show_hidden:
|
||||||
# Default Red behavior, can_see includes a can_run check.
|
# Default Red behavior, can_see includes a can_run check.
|
||||||
if await obj.can_see(ctx):
|
if await obj.can_see(ctx):
|
||||||
yield obj
|
yield obj
|
||||||
elif self.VERIFY_CHECKS:
|
elif verify_checks:
|
||||||
if await obj.can_run(ctx):
|
try:
|
||||||
|
can_run = await obj.can_run(ctx)
|
||||||
|
except discord.DiscordException:
|
||||||
|
can_run = False
|
||||||
|
if can_run:
|
||||||
yield obj
|
yield obj
|
||||||
elif not (self.SHOW_HIDDEN or bypass_hidden):
|
elif not show_hidden:
|
||||||
if getattr(obj, "hidden", False): # Cog compatibility
|
if not getattr(obj, "hidden", False): # Cog compatibility
|
||||||
yield obj
|
yield obj
|
||||||
else:
|
else:
|
||||||
yield obj
|
yield obj
|
||||||
@ -426,17 +449,17 @@ class RedHelpFormatter:
|
|||||||
if fuzzy_commands:
|
if fuzzy_commands:
|
||||||
ret = await format_fuzzy_results(ctx, fuzzy_commands, embed=use_embeds)
|
ret = await format_fuzzy_results(ctx, fuzzy_commands, embed=use_embeds)
|
||||||
if use_embeds:
|
if use_embeds:
|
||||||
ret.set_author()
|
ret.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url)
|
||||||
tagline = (await ctx.bot.db.help.tagline()) or self.get_default_tagline(ctx)
|
tagline = (await ctx.bot.db.help.tagline()) or self.get_default_tagline(ctx)
|
||||||
ret.set_footer(text=tagline)
|
ret.set_footer(text=tagline)
|
||||||
await ctx.send(embed=ret)
|
await ctx.send(embed=ret)
|
||||||
else:
|
else:
|
||||||
await ctx.send(ret)
|
await ctx.send(ret)
|
||||||
elif self.CONFIRM_UNAVAILABLE_COMMAND_EXISTENCES:
|
elif await ctx.bot.db.help.verify_exists():
|
||||||
ret = T_("Command *{command_name}* not found.").format(command_name=command_name)
|
ret = T_("Help topic for *{command_name}* not found.").format(command_name=help_for)
|
||||||
if use_embeds:
|
if use_embeds:
|
||||||
emb = discord.Embed(color=(await ctx.embed_color()), description=ret)
|
ret = discord.Embed(color=(await ctx.embed_color()), description=ret)
|
||||||
emb.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url)
|
ret.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url)
|
||||||
tagline = (await ctx.bot.db.help.tagline()) or self.get_default_tagline(ctx)
|
tagline = (await ctx.bot.db.help.tagline()) or self.get_default_tagline(ctx)
|
||||||
ret.set_footer(text=tagline)
|
ret.set_footer(text=tagline)
|
||||||
await ctx.send(embed=ret)
|
await ctx.send(embed=ret)
|
||||||
@ -447,9 +470,16 @@ class RedHelpFormatter:
|
|||||||
"""
|
"""
|
||||||
Sends an error
|
Sends an error
|
||||||
"""
|
"""
|
||||||
ret = T_("Command *{command_name}* has no subcommands.").format(
|
ret = T_("Command *{command_name}* has no subcommand named *{not_found}*.").format(
|
||||||
command_name=command.qualified_name
|
command_name=command.qualified_name, not_found=not_found[0]
|
||||||
)
|
)
|
||||||
|
if await ctx.embed_requested():
|
||||||
|
ret = discord.Embed(color=(await ctx.embed_color()), description=ret)
|
||||||
|
ret.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url)
|
||||||
|
tagline = (await ctx.bot.db.help.tagline()) or self.get_default_tagline(ctx)
|
||||||
|
ret.set_footer(text=tagline)
|
||||||
|
await ctx.send(embed=ret)
|
||||||
|
else:
|
||||||
await ctx.send(ret)
|
await ctx.send(ret)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -489,19 +519,40 @@ class RedHelpFormatter:
|
|||||||
Sends pages based on settings.
|
Sends pages based on settings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self.USE_MENU:
|
if not (
|
||||||
|
ctx.channel.permissions_for(ctx.me).add_reactions and await ctx.bot.db.help.use_menus()
|
||||||
|
):
|
||||||
|
|
||||||
max_pages_in_guild = await ctx.bot.db.help.max_pages_in_guild()
|
max_pages_in_guild = await ctx.bot.db.help.max_pages_in_guild()
|
||||||
destination = ctx.author if len(pages) > max_pages_in_guild else ctx
|
destination = ctx.author if len(pages) > max_pages_in_guild else ctx
|
||||||
|
|
||||||
if embed:
|
if embed:
|
||||||
for page in pages:
|
for page in pages:
|
||||||
|
try:
|
||||||
await destination.send(embed=page)
|
await destination.send(embed=page)
|
||||||
|
except discord.Forbidden:
|
||||||
|
await ctx.send(
|
||||||
|
T_(
|
||||||
|
"I couldn't send the help message to you in DM. "
|
||||||
|
"Either you blocked me or you disabled DMs in this server."
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
for page in pages:
|
for page in pages:
|
||||||
|
try:
|
||||||
await destination.send(page)
|
await destination.send(page)
|
||||||
|
except discord.Forbidden:
|
||||||
|
await ctx.send(
|
||||||
|
T_(
|
||||||
|
"I couldn't send the help message to you in DM. "
|
||||||
|
"Either you blocked me or you disabled DMs in this server."
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
|
if len(pages) > 1:
|
||||||
await menus.menu(ctx, pages, menus.DEFAULT_CONTROLS)
|
await menus.menu(ctx, pages, menus.DEFAULT_CONTROLS)
|
||||||
|
else:
|
||||||
|
await menus.menu(ctx, pages, {"\N{CROSS MARK}": menus.close_menu})
|
||||||
|
|
||||||
|
|
||||||
@commands.command(name="help", hidden=True, i18n=T_)
|
@commands.command(name="help", hidden=True, i18n=T_)
|
||||||
|
|||||||
@ -529,6 +529,7 @@ class Core(commands.Cog, CoreLogic):
|
|||||||
"""Loads packages"""
|
"""Loads packages"""
|
||||||
if not cogs:
|
if not cogs:
|
||||||
return await ctx.send_help()
|
return await ctx.send_help()
|
||||||
|
cogs = tuple(map(lambda cog: cog.rstrip(","), cogs))
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
loaded, failed, not_found, already_loaded, failed_with_reason = await self._load(cogs)
|
loaded, failed, not_found, already_loaded, failed_with_reason = await self._load(cogs)
|
||||||
|
|
||||||
@ -571,6 +572,7 @@ class Core(commands.Cog, CoreLogic):
|
|||||||
"""Unloads packages"""
|
"""Unloads packages"""
|
||||||
if not cogs:
|
if not cogs:
|
||||||
return await ctx.send_help()
|
return await ctx.send_help()
|
||||||
|
cogs = tuple(map(lambda cog: cog.rstrip(","), cogs))
|
||||||
unloaded, failed = await self._unload(cogs)
|
unloaded, failed = await self._unload(cogs)
|
||||||
|
|
||||||
if unloaded:
|
if unloaded:
|
||||||
@ -589,6 +591,7 @@ class Core(commands.Cog, CoreLogic):
|
|||||||
"""Reloads packages"""
|
"""Reloads packages"""
|
||||||
if not cogs:
|
if not cogs:
|
||||||
return await ctx.send_help()
|
return await ctx.send_help()
|
||||||
|
cogs = tuple(map(lambda cog: cog.rstrip(","), cogs))
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
loaded, failed, not_found, already_loaded, failed_with_reason = await self._reload(
|
loaded, failed, not_found, already_loaded, failed_with_reason = await self._reload(
|
||||||
cogs
|
cogs
|
||||||
@ -1080,6 +1083,80 @@ class Core(commands.Cog, CoreLogic):
|
|||||||
"""Manage settings for the help command."""
|
"""Manage settings for the help command."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@helpset.command(name="usemenus")
|
||||||
|
async def helpset_usemenus(self, ctx: commands.Context, use_menus: bool = None):
|
||||||
|
"""
|
||||||
|
Allows the help command to be sent as a paginated menu instead of seperate
|
||||||
|
messages.
|
||||||
|
|
||||||
|
This defaults to False.
|
||||||
|
Using this without a setting will toggle.
|
||||||
|
"""
|
||||||
|
if use_menus is None:
|
||||||
|
use_menus = not await ctx.bot.db.help.use_menus()
|
||||||
|
await ctx.bot.db.help.use_menus.set(use_menus)
|
||||||
|
if use_menus:
|
||||||
|
await ctx.send(_("Help will use menus."))
|
||||||
|
else:
|
||||||
|
await ctx.send(_("Help will not use menus."))
|
||||||
|
|
||||||
|
@helpset.command(name="showhidden")
|
||||||
|
async def helpset_showhidden(self, ctx: commands.Context, show_hidden: bool = None):
|
||||||
|
"""
|
||||||
|
This allows the help command to show hidden commands
|
||||||
|
|
||||||
|
This defaults to False.
|
||||||
|
Using this without a setting will toggle.
|
||||||
|
"""
|
||||||
|
if show_hidden is None:
|
||||||
|
show_hidden = not await ctx.bot.db.help.show_hidden()
|
||||||
|
await ctx.bot.db.help.show_hidden.set(show_hidden)
|
||||||
|
if show_hidden:
|
||||||
|
await ctx.send(_("Help will not filter hidden commands"))
|
||||||
|
else:
|
||||||
|
await ctx.send(_("Help will filter hidden commands."))
|
||||||
|
|
||||||
|
@helpset.command(name="verifychecks")
|
||||||
|
async def helpset_permfilter(self, ctx: commands.Context, verify: bool = None):
|
||||||
|
"""
|
||||||
|
Sets if commands which can't be run in the current context should be
|
||||||
|
filtered from help
|
||||||
|
|
||||||
|
Defaults to True.
|
||||||
|
Using this without a setting will toggle.
|
||||||
|
"""
|
||||||
|
if verify is None:
|
||||||
|
verify = not await ctx.bot.db.help.verify_checks()
|
||||||
|
await ctx.bot.db.help.verify_checks.set(verify)
|
||||||
|
if verify:
|
||||||
|
await ctx.send(_("Help will only show for commands which can be run."))
|
||||||
|
else:
|
||||||
|
await ctx.send(_("Help will show up without checking if the commands can be run."))
|
||||||
|
|
||||||
|
@helpset.command(name="verifyexists")
|
||||||
|
async def helpset_verifyexists(self, ctx: commands.Context, verify: bool = None):
|
||||||
|
"""
|
||||||
|
This allows the bot to respond indicating the existence of a specific
|
||||||
|
help topic even if the user can't use it.
|
||||||
|
|
||||||
|
Note: This setting on it's own does not fully prevent command enumeration.
|
||||||
|
|
||||||
|
Defaults to False.
|
||||||
|
Using this without a setting will toggle.
|
||||||
|
"""
|
||||||
|
if verify is None:
|
||||||
|
verify = not await ctx.bot.db.help.verify_exists()
|
||||||
|
await ctx.bot.db.help.verify_exists.set(verify)
|
||||||
|
if verify:
|
||||||
|
await ctx.send(_("Help will verify the existence of help topics."))
|
||||||
|
else:
|
||||||
|
await ctx.send(
|
||||||
|
_(
|
||||||
|
"Help will only verify the existence of "
|
||||||
|
"help topics via fuzzy help (if enabled)."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@helpset.command(name="pagecharlimit")
|
@helpset.command(name="pagecharlimit")
|
||||||
async def helpset_pagecharlimt(self, ctx: commands.Context, limit: int):
|
async def helpset_pagecharlimt(self, ctx: commands.Context, limit: int):
|
||||||
"""Set the character limit for each page in the help message.
|
"""Set the character limit for each page in the help message.
|
||||||
@ -1602,7 +1679,7 @@ class Core(commands.Cog, CoreLogic):
|
|||||||
"""
|
"""
|
||||||
user = isinstance(user_or_role, discord.Member)
|
user = isinstance(user_or_role, discord.Member)
|
||||||
|
|
||||||
if user and await ctx.bot.is_owner(obj):
|
if user and await ctx.bot.is_owner(user_or_role):
|
||||||
await ctx.send(_("You cannot blacklist an owner!"))
|
await ctx.send(_("You cannot blacklist an owner!"))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@ -126,7 +126,7 @@ def init_events(bot, cli_flags):
|
|||||||
owners.append(owner)
|
owners.append(owner)
|
||||||
|
|
||||||
for co_owner in bot._co_owners:
|
for co_owner in bot._co_owners:
|
||||||
co_owner = await bot.get_user(co_owner)
|
co_owner = bot.get_user(co_owner)
|
||||||
if co_owner is not None:
|
if co_owner is not None:
|
||||||
owners.append(co_owner)
|
owners.append(co_owner)
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ def init_events(bot, cli_flags):
|
|||||||
await ctx.send(error.args[0])
|
await ctx.send(error.args[0])
|
||||||
else:
|
else:
|
||||||
await ctx.send_help()
|
await ctx.send_help()
|
||||||
elif isinstance(error, commands.BadArgument):
|
elif isinstance(error, commands.UserInputError):
|
||||||
await ctx.send_help()
|
await ctx.send_help()
|
||||||
elif isinstance(error, commands.DisabledCommand):
|
elif isinstance(error, commands.DisabledCommand):
|
||||||
disabled_message = await bot.db.disabled_command_msg()
|
disabled_message = await bot.db.disabled_command_msg()
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
# Ported to Red V3 by Palm\_\_ (https://github.com/palmtree5)
|
# Ported to Red V3 by Palm\_\_ (https://github.com/palmtree5)
|
||||||
import asyncio
|
import asyncio
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import functools
|
||||||
from typing import Union, Iterable, Optional
|
from typing import Union, Iterable, Optional
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
@ -60,7 +61,10 @@ async def menu(
|
|||||||
):
|
):
|
||||||
raise RuntimeError("All pages must be of the same type")
|
raise RuntimeError("All pages must be of the same type")
|
||||||
for key, value in controls.items():
|
for key, value in controls.items():
|
||||||
if not asyncio.iscoroutinefunction(value):
|
maybe_coro = value
|
||||||
|
if isinstance(value, functools.partial):
|
||||||
|
maybe_coro = value.func
|
||||||
|
if not asyncio.iscoroutinefunction(maybe_coro):
|
||||||
raise RuntimeError("Function must be a coroutine")
|
raise RuntimeError("Function must be a coroutine")
|
||||||
current_page = pages[page]
|
current_page = pages[page]
|
||||||
|
|
||||||
|
|||||||
@ -254,7 +254,7 @@ async def reset_red():
|
|||||||
"please select option 5 in the launcher."
|
"please select option 5 in the launcher."
|
||||||
)
|
)
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
print("\nIf you continue you will remove these instanes.\n")
|
print("\nIf you continue you will remove these instances.\n")
|
||||||
for instance in list(instances.keys()):
|
for instance in list(instances.keys()):
|
||||||
print(" - {}".format(instance))
|
print(" - {}".format(instance))
|
||||||
await asyncio.sleep(3)
|
await asyncio.sleep(3)
|
||||||
|
|||||||
@ -440,7 +440,7 @@ async def remove_instance(instance):
|
|||||||
collection = await db.get_collection(name)
|
collection = await db.get_collection(name)
|
||||||
await collection.drop()
|
await collection.drop()
|
||||||
else:
|
else:
|
||||||
pth = Path(instance_data["DATA_PATH"])
|
pth = Path(instance_vals["DATA_PATH"])
|
||||||
safe_delete(pth)
|
safe_delete(pth)
|
||||||
save_config(instance, {}, remove=True)
|
save_config(instance, {}, remove=True)
|
||||||
print("The instance {} has been removed\n".format(instance))
|
print("The instance {} has been removed\n".format(instance))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user