[V3 Fuzzy search] fix several issues with this feature (#1788)

* [V3 Fuzzy search] fix several issues with this feature

* Make it check if parent commands are hidden

* Check if compiler available in setup.py

* Let's just compile a dummy C file to check compiler availability

* Add a missing import + remove unneeded code
This commit is contained in:
palmtree5 2018-06-05 12:14:11 -08:00 committed by Kowlin
parent db5d4d5158
commit 0b78664792
7 changed files with 160 additions and 20 deletions

View File

@ -50,6 +50,7 @@ class RedBase(BotBase):
locale="en",
embeds=True,
color=15158332,
fuzzy=False,
help__page_char_limit=1000,
help__max_pages_in_guild=2,
help__tagline="",
@ -63,6 +64,7 @@ class RedBase(BotBase):
mod_role=None,
embeds=None,
use_bot_color=False,
fuzzy=False,
)
self.db.register_user(embeds=None)

View File

@ -48,6 +48,21 @@ class Command(commands.Command):
# We don't want our help property to be overwritten, namely by super()
pass
@property
def parents(self):
"""
Returns all parent commands of this command.
This is a list, sorted by the length of :attr:`.qualified_name` from highest to lowest.
If the command has no parents, this will be an empty list.
"""
cmd = self.parent
entries = []
while cmd is not None:
entries.append(cmd)
cmd = cmd.parent
return sorted(entries, key=lambda x: len(x.qualified_name), reverse=True)
def command(self, cls=None, *args, **kwargs):
"""A shortcut decorator that invokes :func:`.command` and adds it to
the internal command list via :meth:`~.GroupMixin.add_command`.

View File

@ -653,6 +653,39 @@ class Core(CoreLogic):
)
)
@_set.command()
@checks.guildowner()
@commands.guild_only()
async def serverfuzzy(self, ctx):
"""
Toggle whether to enable fuzzy command search for the server.
Default is for fuzzy command search to be disabled.
"""
current_setting = await ctx.bot.db.guild(ctx.guild).fuzzy()
await ctx.bot.db.guild(ctx.guild).fuzzy.set(not current_setting)
await ctx.send(
_("Fuzzy command search has been {} for this server.").format(
_("disabled") if current_setting else _("enabled")
)
)
@_set.command()
@checks.is_owner()
async def fuzzy(self, ctx):
"""
Toggle whether to enable fuzzy command search in DMs.
Default is for fuzzy command search to be disabled.
"""
current_setting = await ctx.bot.db.fuzzy()
await ctx.bot.db.fuzzy.set(not current_setting)
await ctx.send(
_("Fuzzy command search has been {} in DMs.").format(
_("disabled") if current_setting else _("enabled")
)
)
@_set.command(aliases=["color"])
@checks.is_owner()
async def colour(self, ctx, *, colour: discord.Colour = None):

View File

@ -225,7 +225,9 @@ def init_events(bot, cli_flags):
term = ctx.invoked_with + " "
if len(ctx.args) > 1:
term += " ".join(ctx.args[1:])
await ctx.maybe_send_embed(fuzzy_command_search(ctx, ctx.invoked_with))
fuzzy_result = await fuzzy_command_search(ctx, ctx.invoked_with)
if fuzzy_result is not None:
await ctx.maybe_send_embed(fuzzy_result)
elif isinstance(error, commands.CheckFailure):
pass
elif isinstance(error, commands.NoPrivateMessage):

View File

@ -281,11 +281,10 @@ class Help(formatter.HelpFormatter):
embed.set_author(**self.author)
return embed
async def cmd_not_found(self, ctx, cmd, color=None):
async def cmd_not_found(self, ctx, cmd, description=None, color=None):
# Shortcut for a shortcut. Sue me
out = fuzzy_command_search(ctx, " ".join(ctx.args[1:]))
embed = await self.simple_embed(
ctx, title="Command {} not found.".format(cmd), description=out, color=color
ctx, title="Command {} not found.".format(cmd), description=description, color=color
)
return embed
@ -326,10 +325,18 @@ async def help(ctx, *cmds: str):
command = ctx.bot.all_commands.get(name)
if command is None:
if use_embeds:
await destination.send(embed=await ctx.bot.formatter.cmd_not_found(ctx, name))
else:
fuzzy_result = await fuzzy_command_search(ctx, name)
if fuzzy_result is not None:
await destination.send(
ctx.bot.command_not_found.format(name, fuzzy_command_search(ctx, name))
embed=await ctx.bot.formatter.cmd_not_found(
ctx, name, description=fuzzy_result
)
)
else:
fuzzy_result = await fuzzy_command_search(ctx, name)
if fuzzy_result is not None:
await destination.send(
ctx.bot.command_not_found.format(name, fuzzy_result)
)
return
if use_embeds:
@ -341,11 +348,17 @@ async def help(ctx, *cmds: str):
command = ctx.bot.all_commands.get(name)
if command is None:
if use_embeds:
await destination.send(embed=await ctx.bot.formatter.cmd_not_found(ctx, name))
else:
fuzzy_result = await fuzzy_command_search(ctx, name)
if fuzzy_result is not None:
await destination.send(
ctx.bot.command_not_found.format(name, fuzzy_command_search(ctx, name))
embed=await ctx.bot.formatter.cmd_not_found(
ctx, name, description=fuzzy_result
)
)
else:
fuzzy_result = await fuzzy_command_search(ctx, name)
if fuzzy_result is not None:
await destination.send(ctx.bot.command_not_found.format(name, fuzzy_result))
return
for key in cmds[1:]:
@ -354,12 +367,18 @@ async def help(ctx, *cmds: str):
command = command.all_commands.get(key)
if command is None:
if use_embeds:
fuzzy_result = await fuzzy_command_search(ctx, name)
if fuzzy_result is not None:
await destination.send(
embed=await ctx.bot.formatter.cmd_not_found(ctx, key)
embed=await ctx.bot.formatter.cmd_not_found(
ctx, name, description=fuzzy_result
)
)
else:
fuzzy_result = await fuzzy_command_search(ctx, name)
if fuzzy_result is not None:
await destination.send(
ctx.bot.command_not_found.format(key, fuzzy_command_search(ctx, name))
ctx.bot.command_not_found.format(name, fuzzy_result)
)
return
except AttributeError:

View File

@ -3,11 +3,19 @@ __all__ = ["safe_delete", "fuzzy_command_search"]
from pathlib import Path
import os
import shutil
import logging
from redbot.core import commands
from fuzzywuzzy import process
from .chat_formatting import box
def fuzzy_filter(record):
return record.funcName != "extractWithoutOrder"
logging.getLogger().addFilter(fuzzy_filter)
def safe_delete(pth: Path):
if pth.exists():
for root, dirs, files in os.walk(str(pth)):
@ -19,9 +27,49 @@ def safe_delete(pth: Path):
shutil.rmtree(str(pth), ignore_errors=True)
def fuzzy_command_search(ctx: commands.Context, term: str):
async def filter_commands(ctx: commands.Context, extracted: list):
return [
i
for i in extracted
if i[1] >= 90
and not i[0].hidden
and await i[0].can_run(ctx)
and all([await p.can_run(ctx) for p in i[0].parents])
and not any([p.hidden for p in i[0].parents])
]
async def fuzzy_command_search(ctx: commands.Context, term: str):
out = ""
for pos, extracted in enumerate(process.extract(term, ctx.bot.walk_commands(), limit=5), 1):
if ctx.guild is not None:
enabled = await ctx.bot.db.guild(ctx.guild).fuzzy()
else:
enabled = await ctx.bot.db.fuzzy()
if not enabled:
return None
alias_cog = ctx.bot.get_cog("Alias")
if alias_cog is not None:
is_alias, alias = await alias_cog.is_alias(ctx.guild, term)
if is_alias:
return None
customcom_cog = ctx.bot.get_cog("CustomCommands")
if customcom_cog is not None:
cmd_obj = customcom_cog.commandobj
try:
ccinfo = await cmd_obj.get(ctx.message, term)
except:
pass
else:
return None
extracted_cmds = await filter_commands(
ctx, process.extract(term, ctx.bot.walk_commands(), limit=5)
)
if not extracted_cmds:
return None
for pos, extracted in enumerate(extracted_cmds, 1):
out += "{0}. {1.prefix}{2.qualified_name}{3}\n".format(
pos,
ctx,

View File

@ -1,6 +1,10 @@
from distutils.core import setup
from distutils import ccompiler
from distutils.errors import CCompilerError
from pathlib import Path
import re
import importlib
import tempfile
import os
import sys
@ -21,6 +25,20 @@ def get_package_list():
return core
def check_compiler_available():
m = ccompiler.new_compiler()
with tempfile.TemporaryDirectory() as tdir:
with tempfile.NamedTemporaryFile(prefix="dummy", suffix=".c", dir=tdir) as tfile:
tfile.write(b"int main(int argc, char** argv) {return 0;}")
tfile.seek(0)
try:
m.compile([tfile.name])
except CCompilerError:
return False
return True
def get_requirements():
with open("requirements.txt") as f:
requirements = f.read().splitlines()
@ -31,6 +49,9 @@ def get_requirements():
except ValueError:
pass
if not check_compiler_available(): # Can't compile python-Levensthein, so drop extra
requirements.remove("fuzzywuzzy[speedup]<=0.16.0")
requirements.append("fuzzywuzzy<=0.16.0")
if IS_DEPLOYING or not (IS_TRAVIS or IS_RTD):
requirements.append("discord.py>=1.0.0a0")
if sys.platform.startswith("linux"):