async def repl(self, ctx): """ Open an interactive REPL. The REPL will only recognise code as messages which start with a backtick. This includes codeblocks, and as such multiple lines can be evaluated. """ if ctx.channel.id in self.sessions: await ctx.send( _("Already running a REPL session in this channel. Exit it with `quit`.") ) return variables = Env.from_context(ctx) compiler = Compiler() self.sessions[ctx.channel.id] = True await ctx.send(_("Enter code to execute or evaluate. `exit()` or `quit` to exit.")) while True: response = await ctx.bot.wait_for("message", check=MessagePredicate.regex(r"^`", ctx)) if ctx.channel.id not in self.sessions: return if not self.sessions[ctx.channel.id]: continue cleaned = self.cleanup_code(response.content).strip() if cleaned.rstrip("( )") in ("quit", "exit", "stop"): del self.sessions[ctx.channel.id] await ctx.send(_("Exiting.")) return if cleaned.startswith("from __future__ import"): try: cleaned = self.handle_future(cleaned, compiler) except SyntaxError as e: asyncio.ensure_future( self.send_interactive( ctx, "# Exception:\nTraceback (most recent call last):\n" + "".join(traceback.format_exception_only(type(e), e)), ) ) continue await self.my_exec( ctx, cleaned, variables, "eval", "single", "exec", compiler=compiler, message=response, )
async def repl(self, ctx: commands.Context): if ctx.channel.id in self.sessions: if self.sessions[ctx.channel.id]: await ctx.send( _("Already running a REPL session in this channel. Exit it with `quit`." )) else: await ctx.send( _("Already running a REPL session in this channel. Resume the REPL with `{}repl resume`." ).format(ctx.clean_prefix)) return variables = self.get_environment(ctx) compiler = Compiler() self.sessions[ctx.channel.id] = True await ctx.send( _("Enter code to execute or evaluate. `exit` or `quit` to exit. `{}repl pause` to pause." ).format(ctx.clean_prefix)) while True: response = await ctx.bot.wait_for("message", check=MessagePredicate.regex( r"^`", ctx)) if ctx.channel.id not in self.sessions: return if not self.sessions[ctx.channel.id]: continue cleaned = self.cleanup_code(response.content) exited = await self.my_exec( ctx, cleaned, variables, compiler=compiler, message=response, ) if exited: del self.sessions[ctx.channel.id] await ctx.send(_("Exiting.")) return
async def get_option(self, ctx: commands.Context) -> SlashOption: name_desc = ( "What should the argument name be?\n" "Slash argument names may not exceed 32 characters and can only contain characters " "that are alphanumeric or '_' or '-'.") name_pred = MessagePredicate.regex(SLASH_NAME, ctx) await self.send_and_query_response(ctx, name_desc, name_pred) title = name_pred.result.group(1) description = await self.send_and_query_response( ctx, "What should the argument description be? (maximum 100 characters)", MessagePredicate.length_less(101, ctx), ) valid_option_types = [ name.lower() for name in SlashOptionType.__members__.keys() if not name.startswith("SUB") ] option_query = [ "What should the argument type be?", f"Valid option types: {humanize_list([inline(n) for n in valid_option_types])}", "(select `string` if you don't understand)", ] option_type = await self.send_and_query_response( ctx, "\n".join(option_query), MessagePredicate.lower_contained_in(valid_option_types, ctx), ) option_type = SlashOptionType[option_type.upper()] pred = MessagePredicate.yes_or_no(ctx) await self.send_and_query_response(ctx, "Is this argument required? (Y/n)", pred) required = pred.result return SlashOption(name=title, description=description, option_type=option_type, required=required)
async def get_option( self, ctx: commands.Context, *, added_required: bool = False ) -> SlashOption: name_desc = [ "What should the argument name be and description be?", "The argument name and description should be split by a `:`.", "Example: `member:A member of this server.`\n", "*Slash argument names may not exceed 32 characters and can only contain characters " "that are alphanumeric or '_' or '-'.", "The argument description must be less than or equal to 100 characters.*", ] name_pred = MessagePredicate.regex(ARGUMENT_NAME_DESCRIPTION, ctx) await self.send_and_query_response(ctx, "\n".join(name_desc), name_pred) match = name_pred.result name, description = match.group(1), match.group(2) valid_option_types = [ name.lower() for name in SlashOptionType.__members__.keys() if not name.startswith("SUB") ] valid_option_types.append("choices") option_query = [ "What should the argument type be?", f"Valid option types: {humanize_list([inline(n) for n in valid_option_types])}", "(select `string` if you don't understand)", ] option_type = await self.send_and_query_response( ctx, "\n".join(option_query), MessagePredicate.lower_contained_in(valid_option_types, ctx), ) if option_type == "choices": choices = await self.get_choices(ctx) option_type = "STRING" else: choices = [] option_type = SlashOptionType[option_type.upper()] if not added_required: pred = MessagePredicate.yes_or_no(ctx) await self.send_and_query_response( ctx, "Is this argument required? (Y/n)\n*Keep in mind that if you choose to make this argument optional, all following arguments must also be optional.*", pred, ) required = pred.result else: await ctx.send( "This argument was automatically made optional as the previous one was optional.", delete_after=15, ) required = False return SlashOption( name=name, description=description, option_type=option_type, required=required, choices=choices, )
async def repl(self, ctx): """Open an interactive REPL. The REPL will only recognise code as messages which start with a backtick. This includes codeblocks, and as such multiple lines can be evaluated. You may not await any code in this REPL unless you define it inside an async function. """ variables = { "ctx": ctx, "bot": ctx.bot, "message": ctx.message, "guild": ctx.guild, "channel": ctx.channel, "author": ctx.author, "_": None, } if ctx.channel.id in self.sessions: await ctx.send( _("Already running a REPL session in this channel. Exit it with `quit`." )) return self.sessions.add(ctx.channel.id) await ctx.send( _("Enter code to execute or evaluate. `exit()` or `quit` to exit.") ) while True: response = await ctx.bot.wait_for("message", check=MessagePredicate.regex( r"^`", ctx)) cleaned = self.cleanup_code(response.content) if cleaned in ("quit", "exit", "exit()"): await ctx.send(_("Exiting.")) self.sessions.remove(ctx.channel.id) return executor = exec if cleaned.count("\n") == 0: # single statement, potentially 'eval' try: code = compile(cleaned, "<repl session>", "eval") except SyntaxError: pass else: executor = eval if executor is exec: try: code = compile(cleaned, "<repl session>", "exec") except SyntaxError as e: await ctx.send(self.get_syntax_error(e)) continue variables["message"] = response stdout = io.StringIO() msg = "" try: with redirect_stdout(stdout): result = executor(code, variables) if inspect.isawaitable(result): result = await result except: value = stdout.getvalue() msg = "{}{}".format(value, traceback.format_exc()) else: value = stdout.getvalue() if result is not None: msg = "{}{}".format(value, result) variables["_"] = result elif value: msg = "{}".format(value) msg = self.sanitize_output(ctx, msg) try: await ctx.send_interactive(self.get_pages(msg), box_lang="py") except discord.Forbidden: pass except discord.HTTPException as e: await ctx.send(_("Unexpected error: `{}`").format(e))
async def command_equalizer_save(self, ctx: commands.Context, eq_preset: str = None): """Save the current eq settings to a preset.""" if not self._player_check(ctx): return await self.send_embed_msg(ctx, title=_("Nothing playing.")) dj_enabled = self._dj_status_cache.setdefault( ctx.guild.id, await self.config.guild(ctx.guild).dj_enabled() ) if dj_enabled and not await self._can_instaskip(ctx, ctx.author): ctx.command.reset_cooldown(ctx) return await self.send_embed_msg( ctx, title=_("Unable To Save Preset"), description=_("You need the DJ role to save equalizer presets."), ) if not eq_preset: await self.send_embed_msg( ctx, title=_("Please enter a name for this equalizer preset.") ) try: eq_name_msg = await self.bot.wait_for( "message", timeout=15.0, check=MessagePredicate.regex(fr"^(?!{re.escape(ctx.prefix)})", ctx), ) eq_preset = eq_name_msg.content.split(" ")[0].strip('"').lower() except asyncio.TimeoutError: ctx.command.reset_cooldown(ctx) return await self.send_embed_msg( ctx, title=_("Unable To Save Preset"), description=_( "No equalizer preset name entered, try the command again later." ), ) eq_preset = eq_preset or "" eq_exists_msg = None eq_preset = eq_preset.lower().lstrip(ctx.prefix) eq_presets = await self.config.custom("EQUALIZER", ctx.guild.id).eq_presets() eq_list = list(eq_presets.keys()) if len(eq_preset) > 20: ctx.command.reset_cooldown(ctx) return await self.send_embed_msg( ctx, title=_("Unable To Save Preset"), description=_("Try the command again with a shorter name."), ) if eq_preset in eq_list: eq_exists_msg = await self.send_embed_msg( ctx, title=_("Preset name already exists, do you want to replace it?") ) start_adding_reactions(eq_exists_msg, ReactionPredicate.YES_OR_NO_EMOJIS) pred = ReactionPredicate.yes_or_no(eq_exists_msg, ctx.author) await self.bot.wait_for("reaction_add", check=pred) if not pred.result: await self._clear_react(eq_exists_msg) embed2 = discord.Embed( colour=await ctx.embed_colour(), title=_("Not saving preset.") ) ctx.command.reset_cooldown(ctx) return await eq_exists_msg.edit(embed=embed2) player = lavalink.get_player(ctx.guild.id) player.store("notify_channel", ctx.channel.id) eq = player.fetch("eq", Equalizer()) to_append = {eq_preset: {"author": ctx.author.id, "bands": eq.bands}} new_eq_presets = {**eq_presets, **to_append} await self.config.custom("EQUALIZER", ctx.guild.id).eq_presets.set(new_eq_presets) embed3 = discord.Embed( colour=await ctx.embed_colour(), title=_("Current equalizer saved to the {preset_name} preset.").format( preset_name=eq_preset ), ) if eq_exists_msg: await self._clear_react(eq_exists_msg) await eq_exists_msg.edit(embed=embed3) else: await self.send_embed_msg(ctx, embed=embed3)
async def repl(self, ctx): """Open an interactive REPL. The REPL will only recognise code as messages which start with a backtick. This includes codeblocks, and as such multiple lines can be evaluated. """ if ctx.channel.id in self.sessions: if self.sessions[ctx.channel.id]: await ctx.send( _("Already running a REPL session in this channel. Exit it with `quit`." )) else: await ctx.send( _("Already running a REPL session in this channel. Resume the REPL with `{}repl resume`." ).format(ctx.prefix)) return env = self.get_environment(ctx) env["__builtins__"] = __builtins__ env["_"] = None self.sessions[ctx.channel.id] = True await ctx.send( _("Enter code to execute or evaluate. `exit()` or `quit` to exit. `{}repl pause` to pause." ).format(ctx.prefix)) while True: response = await ctx.bot.wait_for("message", check=MessagePredicate.regex( r"^`", ctx)) if not self.sessions[ctx.channel.id]: continue cleaned = self.cleanup_code(response.content) if cleaned in ("quit", "exit", "exit()"): await ctx.send(_("Exiting.")) del self.sessions[ctx.channel.id] return executor = None if cleaned.count("\n") == 0: # single statement, potentially 'eval' try: code = self.async_compile(cleaned, "<repl session>", "eval") except SyntaxError: pass else: executor = eval if executor is None: try: code = self.async_compile(cleaned, "<repl session>", "exec") except SyntaxError as e: await ctx.send_interactive(self.get_syntax_error(e), box_lang="py") continue env["message"] = response stdout = io.StringIO() msg = "" try: with redirect_stdout(stdout): if executor is None: result = types.FunctionType(code, env)() else: result = executor(code, env) result = await self.maybe_await(result) except: value = stdout.getvalue() msg = "{}{}".format(value, traceback.format_exc()) else: value = stdout.getvalue() if result is not None: msg = "{}{}".format(value, result) env["_"] = result elif value: msg = "{}".format(value) msg = self.sanitize_output(ctx, msg) try: await ctx.send_interactive(self.get_pages(msg), box_lang="py") except discord.Forbidden: pass except discord.HTTPException as e: await ctx.send(_("Unexpected error: `{}`").format(e))