From 14531086ea71bc2bc465a63344ae725d84f1b644 Mon Sep 17 00:00:00 2001 From: Taaku18 <45324516+Taaku18@users.noreply.github.com> Date: Thu, 7 Nov 2019 02:44:27 -0800 Subject: [PATCH] Improved multicommand alias v3.3.1-dev1 --- CHANGELOG.md | 4 +- bot.py | 2 +- cogs/utility.py | 151 ++++++++++++++++++++---------------------------- core/utils.py | 40 +++++-------- 4 files changed, 81 insertions(+), 116 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f31846610f..b536a4808e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,16 +7,18 @@ This project mostly adheres to [Semantic Versioning](https://semver.org/spec/v2. however, insignificant breaking changes does not guarantee a major version bump, see the reasoning [here](https://github.com/kyb3r/modmail/issues/319). -# v3.3.1-dev0 +# v3.3.1-dev1 ### Added - "enable" and "disable" support for yes or no config vars. - Added "perhaps you meant" section to `?config help`. +- Multi-command alias is now more stable. With support for a single quote escape `\"`. ### Internal - Commit to black format line width max = 99, consistent with pylint. +- Alias parser is rewritten without shlex. # v3.3.0 diff --git a/bot.py b/bot.py index 48910db578..1589ae78ac 100644 --- a/bot.py +++ b/bot.py @@ -1,4 +1,4 @@ -__version__ = "3.3.1-dev0" +__version__ = "3.3.1-dev1" import asyncio import logging diff --git a/cogs/utility.py b/cogs/utility.py index 392656b650..f87b0f552d 100644 --- a/cogs/utility.py +++ b/cogs/utility.py @@ -16,6 +16,7 @@ import discord from discord.enums import ActivityType, Status from discord.ext import commands, tasks +from discord.ext.commands.view import StringView from discord.utils import escape_markdown, escape_mentions from aiohttp import ClientResponseError @@ -1002,6 +1003,7 @@ async def alias_add(self, ctx, name: str.lower, *, value): return await ctx.send(embed=embed) values = utils.parse_alias(value) + save_aliases = [] if not values: embed = discord.Embed( @@ -1012,59 +1014,45 @@ async def alias_add(self, ctx, name: str.lower, *, value): embed.set_footer(text=f'See "{self.bot.prefix}alias add" for more details.') return await ctx.send(embed=embed) - if len(values) == 1: - linked_command, *messages = values[0].split(maxsplit=1) + multiple_alias = len(values) > 1 + + embed = discord.Embed( + title="Added alias", + color=self.bot.main_color + ) + + if multiple_alias: + embed.description = f'`{name}` points to: "{values[0]}".' + else: + embed.description = f"`{name}` now points to the following steps:" + + for i, val in enumerate(values, start=1): + view = StringView(val) + linked_command = view.get_word() + message = view.read_rest() + if not self.bot.get_command(linked_command): alias_command = self.bot.aliases.get(linked_command) if alias_command is not None: - if messages: - values = [f"{alias_command} {messages[0]}"] - else: - values = [alias_command] + save_aliases.append(f"{alias_command} {message}".strip()) else: - embed = discord.Embed( - title="Error", - color=self.bot.error_color, - description="The command you are attempting to point " - f"to does not exist: `{linked_command}`.", - ) - return await ctx.send(embed=embed) + embed = discord.Embed(title="Error", color=self.bot.error_color) - embed = discord.Embed( - title="Added alias", - color=self.bot.main_color, - description=f'`{name}` points to: "{values[0]}".', - ) + if multiple_alias: + embed.description = ("The command you are attempting to point " + f"to does not exist: `{linked_command}`.") + else: + embed.description = ("The command you are attempting to point " + f"to n step {i} does not exist: `{linked_command}`.") - else: - embed = discord.Embed( - title="Added alias", - color=self.bot.main_color, - description=f"`{name}` now points to the following steps:", - ) + return await ctx.send(embed=embed) + else: + save_aliases.append(val) - for i, val in enumerate(values, start=1): - linked_command, *messages = val.split(maxsplit=1) - if not self.bot.get_command(linked_command): - alias_command = self.bot.aliases.get(linked_command) - if alias_command is not None: - if messages: - values = [f"{alias_command} {messages[0]}"] - else: - values = [alias_command] - else: - embed = discord.Embed( - title="Error", - color=self.bot.error_color, - description="The command you are attempting to point " - f"to n step {i} does not exist: `{linked_command}`.", - ) - return await ctx.send(embed=embed) - embed.description += f"\n{i}: {val}" + embed.description += f"\n{i}: {val}" - self.bot.aliases[name] = " && ".join(values) + self.bot.aliases[name] = " && ".join(f"\"{a}\"" for a in save_aliases) await self.bot.config.update() - return await ctx.send(embed=embed) @alias.command(name="remove", aliases=["del", "delete"]) @@ -1097,6 +1085,7 @@ async def alias_edit(self, ctx, name: str.lower, *, value): return await ctx.send(embed=embed) values = utils.parse_alias(value) + save_aliases = [] if not values: embed = discord.Embed( @@ -1107,56 +1096,44 @@ async def alias_edit(self, ctx, name: str.lower, *, value): embed.set_footer(text=f'See "{self.bot.prefix}alias add" for more details.') return await ctx.send(embed=embed) - if len(values) == 1: - linked_command, *messages = values[0].split(maxsplit=1) + multiple_alias = len(values) > 1 + + embed = discord.Embed( + title="Edited alias", + color=self.bot.main_color + ) + + if multiple_alias: + embed.description = f'`{name}` points to: "{values[0]}".' + else: + embed.description = f"`{name}` now points to the following steps:" + + for i, val in enumerate(values, start=1): + view = StringView(val) + linked_command = view.get_word() + message = view.read_rest() + if not self.bot.get_command(linked_command): alias_command = self.bot.aliases.get(linked_command) if alias_command is not None: - if messages: - values = [f"{alias_command} {messages[0]}"] - else: - values = [alias_command] + save_aliases.append(f"{alias_command} {message}".strip()) else: - embed = discord.Embed( - title="Error", - color=self.bot.error_color, - description="The command you are attempting to point " - f"to does not exist: `{linked_command}`.", - ) - return await ctx.send(embed=embed) - embed = discord.Embed( - title="Edited alias", - color=self.bot.main_color, - description=f'`{name}` now points to: "{values[0]}".', - ) + embed = discord.Embed(title="Error", color=self.bot.error_color) - else: - embed = discord.Embed( - title="Edited alias", - color=self.bot.main_color, - description=f"`{name}` now points to the following steps:", - ) - - for i, val in enumerate(values, start=1): - linked_command, *messages = val.split(maxsplit=1) - if not self.bot.get_command(linked_command): - alias_command = self.bot.aliases.get(linked_command) - if alias_command is not None: - if messages: - values = [f"{alias_command} {messages[0]}"] - else: - values = [alias_command] + if multiple_alias: + embed.description = ("The command you are attempting to point " + f"to does not exist: `{linked_command}`.") else: - embed = discord.Embed( - title="Error", - color=self.bot.error_color, - description="The command you are attempting to point " - f"to on step {i} does not exist: `{linked_command}`.", - ) - return await ctx.send(embed=embed) - embed.description += f"\n{i}: {val}" + embed.description = ("The command you are attempting to point " + f"to n step {i} does not exist: `{linked_command}`.") + + return await ctx.send(embed=embed) + else: + save_aliases.append(val) + + embed.description += f"\n{i}: {val}" - self.bot.aliases[name] = "&&".join(values) + self.bot.aliases[name] = " && ".join(f"\"{a}\"" for a in save_aliases) await self.bot.config.update() return await ctx.send(embed=embed) diff --git a/core/utils.py b/core/utils.py index 68c0870c71..23a89428b1 100644 --- a/core/utils.py +++ b/core/utils.py @@ -1,6 +1,6 @@ +import base64 import functools import re -import shlex import typing from difflib import get_close_matches from distutils.util import strtobool as _stb # pylint: disable=import-error @@ -215,35 +215,21 @@ def create_not_found_embed(word, possibilities, name, n=2, cutoff=0.6) -> discor def parse_alias(alias): - if "&&" not in alias: - if alias.startswith('"') and alias.endswith('"'): - return [alias[1:-1]] - return [alias] + def encode_alias(m): + return "\x1AU" + base64.b64encode(m.group(1).encode()).decode() + "\x1AU" - buffer = "" - cmd = [] - try: - for token in shlex.shlex(alias, punctuation_chars="&"): - if token != "&&": - buffer += " " + token - continue - - buffer = buffer.strip() - if buffer.startswith('"') and buffer.endswith('"'): - buffer = buffer[1:-1] - cmd += [buffer] - buffer = "" - except ValueError: - return [] + def decode_alias(m): + return base64.b64decode(m.group(1).encode()).decode() + + alias = re.sub(r"(?:(?<=^)(?:\s*(?