Skip to content

Commit 1453108

Browse files
committed
Improved multicommand alias v3.3.1-dev1
1 parent de71ab2 commit 1453108

File tree

4 files changed

+81
-116
lines changed

4 files changed

+81
-116
lines changed

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ This project mostly adheres to [Semantic Versioning](https://semver.org/spec/v2.
77
however, insignificant breaking changes does not guarantee a major version bump, see the reasoning [here](https://github.com/kyb3r/modmail/issues/319).
88

99

10-
# v3.3.1-dev0
10+
# v3.3.1-dev1
1111

1212
### Added
1313

1414
- "enable" and "disable" support for yes or no config vars.
1515
- Added "perhaps you meant" section to `?config help`.
16+
- Multi-command alias is now more stable. With support for a single quote escape `\"`.
1617

1718
### Internal
1819

1920
- Commit to black format line width max = 99, consistent with pylint.
21+
- Alias parser is rewritten without shlex.
2022

2123
# v3.3.0
2224

bot.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "3.3.1-dev0"
1+
__version__ = "3.3.1-dev1"
22

33
import asyncio
44
import logging

cogs/utility.py

+64-87
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import discord
1717
from discord.enums import ActivityType, Status
1818
from discord.ext import commands, tasks
19+
from discord.ext.commands.view import StringView
1920
from discord.utils import escape_markdown, escape_mentions
2021

2122
from aiohttp import ClientResponseError
@@ -1002,6 +1003,7 @@ async def alias_add(self, ctx, name: str.lower, *, value):
10021003
return await ctx.send(embed=embed)
10031004

10041005
values = utils.parse_alias(value)
1006+
save_aliases = []
10051007

10061008
if not values:
10071009
embed = discord.Embed(
@@ -1012,59 +1014,45 @@ async def alias_add(self, ctx, name: str.lower, *, value):
10121014
embed.set_footer(text=f'See "{self.bot.prefix}alias add" for more details.')
10131015
return await ctx.send(embed=embed)
10141016

1015-
if len(values) == 1:
1016-
linked_command, *messages = values[0].split(maxsplit=1)
1017+
multiple_alias = len(values) > 1
1018+
1019+
embed = discord.Embed(
1020+
title="Added alias",
1021+
color=self.bot.main_color
1022+
)
1023+
1024+
if multiple_alias:
1025+
embed.description = f'`{name}` points to: "{values[0]}".'
1026+
else:
1027+
embed.description = f"`{name}` now points to the following steps:"
1028+
1029+
for i, val in enumerate(values, start=1):
1030+
view = StringView(val)
1031+
linked_command = view.get_word()
1032+
message = view.read_rest()
1033+
10171034
if not self.bot.get_command(linked_command):
10181035
alias_command = self.bot.aliases.get(linked_command)
10191036
if alias_command is not None:
1020-
if messages:
1021-
values = [f"{alias_command} {messages[0]}"]
1022-
else:
1023-
values = [alias_command]
1037+
save_aliases.append(f"{alias_command} {message}".strip())
10241038
else:
1025-
embed = discord.Embed(
1026-
title="Error",
1027-
color=self.bot.error_color,
1028-
description="The command you are attempting to point "
1029-
f"to does not exist: `{linked_command}`.",
1030-
)
1031-
return await ctx.send(embed=embed)
1039+
embed = discord.Embed(title="Error", color=self.bot.error_color)
10321040

1033-
embed = discord.Embed(
1034-
title="Added alias",
1035-
color=self.bot.main_color,
1036-
description=f'`{name}` points to: "{values[0]}".',
1037-
)
1041+
if multiple_alias:
1042+
embed.description = ("The command you are attempting to point "
1043+
f"to does not exist: `{linked_command}`.")
1044+
else:
1045+
embed.description = ("The command you are attempting to point "
1046+
f"to n step {i} does not exist: `{linked_command}`.")
10381047

1039-
else:
1040-
embed = discord.Embed(
1041-
title="Added alias",
1042-
color=self.bot.main_color,
1043-
description=f"`{name}` now points to the following steps:",
1044-
)
1048+
return await ctx.send(embed=embed)
1049+
else:
1050+
save_aliases.append(val)
10451051

1046-
for i, val in enumerate(values, start=1):
1047-
linked_command, *messages = val.split(maxsplit=1)
1048-
if not self.bot.get_command(linked_command):
1049-
alias_command = self.bot.aliases.get(linked_command)
1050-
if alias_command is not None:
1051-
if messages:
1052-
values = [f"{alias_command} {messages[0]}"]
1053-
else:
1054-
values = [alias_command]
1055-
else:
1056-
embed = discord.Embed(
1057-
title="Error",
1058-
color=self.bot.error_color,
1059-
description="The command you are attempting to point "
1060-
f"to n step {i} does not exist: `{linked_command}`.",
1061-
)
1062-
return await ctx.send(embed=embed)
1063-
embed.description += f"\n{i}: {val}"
1052+
embed.description += f"\n{i}: {val}"
10641053

1065-
self.bot.aliases[name] = " && ".join(values)
1054+
self.bot.aliases[name] = " && ".join(f"\"{a}\"" for a in save_aliases)
10661055
await self.bot.config.update()
1067-
10681056
return await ctx.send(embed=embed)
10691057

10701058
@alias.command(name="remove", aliases=["del", "delete"])
@@ -1097,6 +1085,7 @@ async def alias_edit(self, ctx, name: str.lower, *, value):
10971085
return await ctx.send(embed=embed)
10981086

10991087
values = utils.parse_alias(value)
1088+
save_aliases = []
11001089

11011090
if not values:
11021091
embed = discord.Embed(
@@ -1107,56 +1096,44 @@ async def alias_edit(self, ctx, name: str.lower, *, value):
11071096
embed.set_footer(text=f'See "{self.bot.prefix}alias add" for more details.')
11081097
return await ctx.send(embed=embed)
11091098

1110-
if len(values) == 1:
1111-
linked_command, *messages = values[0].split(maxsplit=1)
1099+
multiple_alias = len(values) > 1
1100+
1101+
embed = discord.Embed(
1102+
title="Edited alias",
1103+
color=self.bot.main_color
1104+
)
1105+
1106+
if multiple_alias:
1107+
embed.description = f'`{name}` points to: "{values[0]}".'
1108+
else:
1109+
embed.description = f"`{name}` now points to the following steps:"
1110+
1111+
for i, val in enumerate(values, start=1):
1112+
view = StringView(val)
1113+
linked_command = view.get_word()
1114+
message = view.read_rest()
1115+
11121116
if not self.bot.get_command(linked_command):
11131117
alias_command = self.bot.aliases.get(linked_command)
11141118
if alias_command is not None:
1115-
if messages:
1116-
values = [f"{alias_command} {messages[0]}"]
1117-
else:
1118-
values = [alias_command]
1119+
save_aliases.append(f"{alias_command} {message}".strip())
11191120
else:
1120-
embed = discord.Embed(
1121-
title="Error",
1122-
color=self.bot.error_color,
1123-
description="The command you are attempting to point "
1124-
f"to does not exist: `{linked_command}`.",
1125-
)
1126-
return await ctx.send(embed=embed)
1127-
embed = discord.Embed(
1128-
title="Edited alias",
1129-
color=self.bot.main_color,
1130-
description=f'`{name}` now points to: "{values[0]}".',
1131-
)
1121+
embed = discord.Embed(title="Error", color=self.bot.error_color)
11321122

1133-
else:
1134-
embed = discord.Embed(
1135-
title="Edited alias",
1136-
color=self.bot.main_color,
1137-
description=f"`{name}` now points to the following steps:",
1138-
)
1139-
1140-
for i, val in enumerate(values, start=1):
1141-
linked_command, *messages = val.split(maxsplit=1)
1142-
if not self.bot.get_command(linked_command):
1143-
alias_command = self.bot.aliases.get(linked_command)
1144-
if alias_command is not None:
1145-
if messages:
1146-
values = [f"{alias_command} {messages[0]}"]
1147-
else:
1148-
values = [alias_command]
1123+
if multiple_alias:
1124+
embed.description = ("The command you are attempting to point "
1125+
f"to does not exist: `{linked_command}`.")
11491126
else:
1150-
embed = discord.Embed(
1151-
title="Error",
1152-
color=self.bot.error_color,
1153-
description="The command you are attempting to point "
1154-
f"to on step {i} does not exist: `{linked_command}`.",
1155-
)
1156-
return await ctx.send(embed=embed)
1157-
embed.description += f"\n{i}: {val}"
1127+
embed.description = ("The command you are attempting to point "
1128+
f"to n step {i} does not exist: `{linked_command}`.")
1129+
1130+
return await ctx.send(embed=embed)
1131+
else:
1132+
save_aliases.append(val)
1133+
1134+
embed.description += f"\n{i}: {val}"
11581135

1159-
self.bot.aliases[name] = "&&".join(values)
1136+
self.bot.aliases[name] = " && ".join(f"\"{a}\"" for a in save_aliases)
11601137
await self.bot.config.update()
11611138
return await ctx.send(embed=embed)
11621139

core/utils.py

+13-27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import base64
12
import functools
23
import re
3-
import shlex
44
import typing
55
from difflib import get_close_matches
66
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
215215

216216

217217
def parse_alias(alias):
218-
if "&&" not in alias:
219-
if alias.startswith('"') and alias.endswith('"'):
220-
return [alias[1:-1]]
221-
return [alias]
218+
def encode_alias(m):
219+
return "\x1AU" + base64.b64encode(m.group(1).encode()).decode() + "\x1AU"
222220

223-
buffer = ""
224-
cmd = []
225-
try:
226-
for token in shlex.shlex(alias, punctuation_chars="&"):
227-
if token != "&&":
228-
buffer += " " + token
229-
continue
230-
231-
buffer = buffer.strip()
232-
if buffer.startswith('"') and buffer.endswith('"'):
233-
buffer = buffer[1:-1]
234-
cmd += [buffer]
235-
buffer = ""
236-
except ValueError:
237-
return []
221+
def decode_alias(m):
222+
return base64.b64decode(m.group(1).encode()).decode()
223+
224+
alias = re.sub(r"(?:(?<=^)(?:\s*(?<!\\)(?:\")\s*)|(?<=&&)(?:\s*(?<!\\)(?:\")\s*))(.+?)"
225+
r"(?:(?:\s*(?<!\\)(?:\")\s*)(?=&&)|(?:\s*(?<!\\)(?:\")\s*)(?=$))",
226+
encode_alias, alias)
238227

239-
buffer = buffer.strip()
240-
if buffer.startswith('"') and buffer.endswith('"'):
241-
buffer = buffer[1:-1]
242-
cmd += [buffer]
228+
aliases = []
229+
for alias in re.split(r"\s*&&\s*", alias):
230+
aliases.append(re.sub("\x1AU(.+?)\x1AU", decode_alias, alias))
243231

244-
if not all(cmd):
245-
return []
246-
return cmd
232+
return aliases
247233

248234

249235
def format_description(i, names):

0 commit comments

Comments
 (0)