def argsplit(args): view = StringView(args) args = [] while not view.eof: view.skip_ws() args.append(quoted_word(view)) return args
def get_extra_args_from_alias( self, message: discord.Message, prefix: str, alias: AliasEntry ) -> str: """ When an alias is executed by a user in chat this function tries to get any extra arguments passed in with the call. Whitespace will be trimmed from both ends. :param message: :param prefix: :param alias: :return: """ known_content_length = len(prefix) + len(alias.name) extra = message.content[known_content_length:] view = StringView(extra) view.skip_ws() extra = [] while not view.eof: prev = view.index word = quoted_word(view) if len(word) < view.index - prev: word = "".join((view.buffer[prev], word, view.buffer[view.index - 1])) extra.append(word) view.skip_ws() return extra
async def on_message(self, message): if message.author.bot or not self.bot.is_running(): return if message.content.startswith(self.bot.command_prefix): name = message.content.lower().lstrip(self.bot.command_prefix).split(" ")[0] if command := config.Commands.fetch(name): attachment = None if command["attachment"]: async with aiohttp.ClientSession() as session: async with session.get(command["attachment"]) as resp: buff = io.BytesIO(await resp.read()) attachment = discord.File(filename=command["attachment"].split("/")[-1], fp=buff) args = [] view = StringView(message.content.lstrip(self.bot.command_prefix)) view.get_word() # command name while not view.eof: view.skip_ws() args.append(view.get_quoted_word()) text = re.sub( r'{(\d+)}', lambda match: args[int(match.group(1))] if int(match.group(1)) < len(args) else '(missing argument)', command["content"] ).replace('{...}', ' '.join(args)) await self.bot.reply_to_msg(message, text, file=attachment) return
async def exec(self, ctx, *, code): """Remote executes code.""" code = code.strip() py_start = code.lower().startswith("```py") python_start = py_start and code.lower().startswith("```python") view = StringView(code) view.skip_string(ctx.prefix) view.skip_ws() view.skip_string(ctx.invoked_with) view.skip_ws() code = view.read_rest() if python_start: code = code[9:] elif py_start: code = code[5:] elif code.startswith("```"): code = code[3:] if code.endswith("```"): code = code[:-3] del py_start, python_start, view with closing(StringIO()) as log: with redirect_stdout(log): try: exec(code) except Exception: error = exc_info() for e in error: print(e) output = log.getvalue() output = "```\n" + output + "```" await reply(ctx, output)
def handle_alias_arguments(self, command, message): """Takes an alias name, alias value, and message and handles percent-encoded args. Returns: string""" rawargs = " ".join(self.bot.prefix.join(message.content.split(self.bot.prefix)[1:]).split(' ')[1:]) view = StringView(rawargs) args = [] while not view.eof: view.skip_ws() args.append(quoted_word(view)) tempargs = args[:] new_command = command if '%*%' in command: new_command = new_command.replace('%*%', shlex.quote(rawargs) if ' ' in rawargs else rawargs) tempargs = [] if '&*&' in command: new_command = new_command.replace('&*&', rawargs.replace("\"", "\\\"").replace("'", "\\'")) tempargs = [] for index, value in enumerate(args): key = '%{}%'.format(index + 1) to_remove = False if key in command: new_command = new_command.replace(key, shlex.quote(value) if ' ' in value else value) to_remove = True key = '&{}&'.format(index + 1) if key in command: new_command = new_command.replace(key, value.replace("\"", "\\\"").replace("'", "\\'")) to_remove = True if to_remove: try: tempargs.remove(value) except ValueError: pass return self.bot.prefix + new_command + " " + ' '.join((shlex.quote(v) if ' ' in v else v) for v in tempargs)
def get_extra_args_from_alias(self, message: discord.Message, prefix: str, alias: AliasEntry) -> str: """ When an alias is executed by a user in chat this function tries to get any extra arguments passed in with the call. Whitespace will be trimmed from both ends. :param message: :param prefix: :param alias: :return: """ known_content_length = len(prefix) + len(alias.name) extra = message.content[known_content_length:] view = StringView(extra) view.skip_ws() extra = [] while not view.eof: prev = view.index word = quoted_word(view) if len(word) < view.index - prev: word = "".join( (view.buffer[prev], word, view.buffer[view.index - 1])) extra.append(word) view.skip_ws() return extra
async def convert(self, ctx, argument): converted = [] view = StringView(argument) while not view.eof: args = [] for converter in self.converters: view.skip_ws() arg = view.get_quoted_word() if arg is None: raise commands.UserInputError(_('Not enough arguments.')) args.append(await self._do_conversion(ctx, converter, arg)) converted.append(tuple(args)) return converted
async def _get_roles_from_content(ctx, content): # greedy = Greedy[RoleConverter] view = StringView(content) rc = RoleConverter() # "Borrowed" from discord.ext.commands.Command._transform_greedy_pos result = [] while not view.eof: # for use with a manual undo previous = view.index view.skip_ws() try: argument = view.get_quoted_word() value = await rc.convert(ctx, argument) except (CommandError, ArgumentParsingError): view.index = previous break else: result.append(value) return [r.id for r in result]
def words(view: StringView) -> Generator[str, None, None]: """Yields each argument from a given StringView.""" while not view.eof: view.skip_ws() yield quoted_word(view)