Ejemplo n.º 1
0
    async def dev_ev(self, ctx: utils.CustomContext, *,
                     code: codeblock_converter):
        """Evaluates Python Code."""

        env = {
            "bot": self.bot,
            "ctx": ctx,
            "channel": ctx.channel,
            "author": ctx.author,
            "guild": ctx.guild,
            "message": ctx.message,
            "_": self._last_result
        }

        env.update(globals())

        body = code.content
        stdout = io.StringIO()

        to_compile = f"async def func():\n{textwrap.indent(body, '  ')}"

        try:
            exec(to_compile, env)
        except Exception as e:
            ret = utils.codeblock(f"{e.__class__.__name__}: {e}")
            return await ctx.send(ret)

        func = env["func"]
        try:
            with redirect_stdout(stdout):
                ret = await func()
        except Exception as e:
            value = stdout.getvalue()
            ret = utils.codeblock(f"{value}{traceback.format_exc()}")
            await ctx.send(ret)
        else:
            value = stdout.getvalue()
            try:
                await ctx.message.add_reaction("\U0001f44c")
            except:
                pass

            if ret is None:
                if value:
                    ret = utils.codeblock(f"{value}")
                    await ctx.send(ret)
            else:
                self._last_result = ret
                ret = utils.codeblock(f"{value}{ret}")
                await ctx.send(ret)
Ejemplo n.º 2
0
    async def plugins(self, ctx):
        """Lists all currently loaded plugins"""

        message = "Plugins loaded:"
        message += "\n".join(self.bot.cogs.keys())
        message = utils.codeblock(message)
        await ctx.send(message)
Ejemplo n.º 3
0
    async def auto(self, ctx, mode: str = ""):
        """
        Prints current mode or turn automatic on/off (on by default)

        Syntax:
        !auto will display the current mode
        !auto on will switch to automatic mode
        !auto off will switch to manual mode
        Automatic mode will disable manual card draw commands
        """

        if not mode:
            # Print current mode
            if ctx.game.automatic:
                message = loc["auto"]["CurrentlyAuto"]
            else:
                message = loc["auto"]["CurrentlyManual"]

            message = utils.codeblock(message)
            asyncio.create_task(ctx.send(message))
        elif mode == loc["auto"]["on"]:
            ctx.game.automatic = True
            asyncio.create_task(ctx.send(loc["auto"]["AutoEnabled"]))
        elif mode == loc["auto"]["off"]:
            ctx.game.automatic = False
            asyncio.create_task(ctx.send(loc["auto"]["AutoDisabled"]))
        else:
            asyncio.create_task(
                ctx.send(LOCALIZATION_DATA["errors"]["UserInputError"]))
Ejemplo n.º 4
0
    async def auto(self, ctx, mode: str=""):
        """
        Prints current mode or turn automatic on/off (on by default)

        Syntax:
        !auto will display the current mode
        !auto on will switch to automatic mode
        !auto off will switch to manual mode
        Automatic mode will disable manual card draw commands
        """

        if not mode:
            # Print current mode
            message = "Current mode: "
            if ctx.game.automatic:
                message += "automatic"
            else:
                message += "manual"
            
            message = utils.codeblock(message)
            await ctx.send(message)
        elif mode == "on":
            ctx.game.automatic = True
            await ctx.send("Automatic card draw enabled!")
        elif mode == "off":
            ctx.game.automatic = False
            await ctx.send("Automatic card draw disabled!")
        else:
            await ctx.send("Input error, try !auto on or !auto off")
Ejemplo n.º 5
0
    def format_page(self, menu: menus.MenuPages,
                    page: tp.List[ExtensionResult]) -> utils.Embed:
        """Formats the page into an embed"""

        embed = utils.Embed(title=self.load_type,
                            color=discord.Color.orange(),
                            default_inline=False)

        for ext_name, error in page:

            clean_ext_name = discord.utils.escape_markdown(ext_name)

            if not isinstance(error, str):

                if isinstance(error, EXTENSIONS_IGNORE
                              ):  # those errors aren't worth a full traceback
                    error = str(error)

                else:
                    error = utils.format_exception(*utils.exc_info(error),
                                                   limit=4)

            embed.add_field(name=clean_ext_name,
                            value=utils.codeblock(error, lang='py')[:1024])

        return embed
Ejemplo n.º 6
0
    async def config_edit(self, ctx: core.Context, *, exec_string: str):
        """Edits the config file"""

        _, exec_string = jishaku.codeblocks.codeblock_converter(exec_string)

        new_dict = self.bot.config.copy()

        env = {'config': new_dict}

        exec(exec_string, env)

        new_config = env['config']

        if not isinstance(new_config, dict):
            raise commands.BadArgument(
                message='The new config must be a dict not ' +
                new_config.__class__.__name__)

        json = orjson.dumps(new_config,
                            option=orjson.OPT_INDENT_2).decode('utf-8')

        await ctx.send('Sent the new config in your dms !')

        codeblock = utils.codeblock(json, lang='json')

        menu = utils.Confirm(msg=codeblock + '\nconfirm changes ?',
                             user=ctx.author)

        await menu.start(ctx, channel=await ctx.author.create_dm(), wait=True)

        if menu.accepted:
            self.bot.config = env['config']

        else:
            await ctx.author.send('Cancelled the config edit')
Ejemplo n.º 7
0
    async def assign_clues(self, ctx):
        """Randomizes and assigns clue times"""

        player_count = len(ctx.game.char_roles())
        # Stop if fewer than 3 player roles assigned
        if player_count < 3:
            await ctx.send("Not enough players!")
            return

        # Can't play without Charlie
        elif "Charlie" not in ctx.game.char_roles():
            await ctx.send("Can't find Charlie!")
            return

        if not ctx.game.automatic:
            asyncio.create_task(ctx.send("Assigning clue cards!"))

        # Generate clues
        while True:
            clue_buckets = self._randomize_clues(player_count)
            if self._test_clue_buckets(ctx, clue_buckets):
                break

        random.shuffle(clue_buckets)

        # Empty buckets
        ctx.game.clue_assignments = {}

        # Give bucket with 90 minute card to Charlie Barnes
        for bucket in clue_buckets:
            if 90 in bucket:
                # Charlie's bucket! Willy Wonka sends his regards
                ctx.game.clue_assignments["charlie"] = sorted(bucket,
                                                              reverse=True)
                clue_buckets.remove(bucket)
                break

        # Assign the rest of the buckets randomly
        names = [name.lower() for name in ctx.game.char_roles()]
        names.remove("charlie")  # Already assigned
        for name in names:
            ctx.game.clue_assignments[name] = sorted(clue_buckets.pop(),
                                                     reverse=True)

        # Print in a code block
        message = "Clue times:\n"
        message += "\n".join([
            f"{player.title()}: {', '.join(str(x) for x in bucket)}"
            for player, bucket in ctx.game.clue_assignments.items()
        ])
        message = utils.codeblock(message)

        channel = ctx.text_channels["player-resources"]
        asyncio.create_task(channel.send(message))

        # Console logging
        print("Randomly assigned clue cards!")
        print(ctx.game.clue_assignments)
Ejemplo n.º 8
0
    async def credits(self, ctx):
        """Prints information about the bot"""

        message = "\n".join([
            "The White Rabbit was created by Chenkai and Chendi Luo.",
            "Source code available at " + utils.SOURCE_URL,
        ])
        message = utils.codeblock(message)
        asyncio.create_task(ctx.send(message))
Ejemplo n.º 9
0
    async def docs(self, ctx):
        """Link to the documentation"""

        message = "\n".join([
            "Documentation for The White Rabbit can be found here:",
            utils.DOCS_SHORT_URL,
        ])
        message = utils.codeblock(message)
        asyncio.create_task(ctx.send(message))
Ejemplo n.º 10
0
    async def docs(self, ctx):
        """Link to the documentation"""

        message = "\n".join([
            "Documentation for The White Rabbit can be found here:",
            "https://white-rabbit.rtfd.io/"
        ])
        message = utils.codeblock(message)
        asyncio.create_task(ctx.send(message))
Ejemplo n.º 11
0
    async def docs(self, ctx):
        """Link to the documentation"""

        message = "\n".join([
            loc["docs"]["documentation"],
            constants.DOCS_SHORT_URL,
        ])
        message = utils.codeblock(message)
        asyncio.create_task(ctx.send(message))
Ejemplo n.º 12
0
def format_roll_results(results):
    out = ""
    for row in results:
        final_value = row.final_repr()
        out += codeblock(row.describe(uneval=True)) +\
            f" ⇒ **{final_value}**" +\
            f"  |  {row.describe()}"
        out += "\n"
    return out
Ejemplo n.º 13
0
    async def credits(self, ctx):
        """Prints information about the bot"""

        message = "\n".join([
            loc["credits"]["creators"],
            loc["credits"]["source"],
            constants.SOURCE_URL,
        ])
        message = utils.codeblock(message)
        asyncio.create_task(ctx.send(message))
Ejemplo n.º 14
0
    async def endings(self, ctx, index: int=0):
        """Enables/disables an ending. See docs for details"""

        if not index:
            # Print out currently enabled endings
            message = "Endings enabled: "
            message += ", ".join([f"#{end}" for end in ctx.game.endings if ctx.game.endings[end]])
            message = utils.codeblock(message)
            await ctx.send(message)
        else:
            # Toggle specified ending
            ctx.game.endings[index] = not ctx.game.endings[index]
Ejemplo n.º 15
0
    async def print_times(self, ctx):
        """
        Print out clue assignments in a code block
        """

        message = loc["print_times"]["ClueTimes"] + "\n"
        message += "\n".join([
            f"{player.title()}: {', '.join(str(x) for x in bucket)}"
            for player, bucket in ctx.game.clue_assignments.items()
        ])
        message = utils.codeblock(message)

        channel = ctx.text_channels[LOCALIZATION_DATA["channels"]["resources"]]
        asyncio.create_task(channel.send(message))
Ejemplo n.º 16
0
    async def config(self, ctx: core.Context):
        """Displays the json config file"""

        encoded = orjson.dumps(self.bot.config, option=orjson.OPT_INDENT_2)

        config = encoded.decode('utf-8')

        codeblock = utils.codeblock(config, lang='json')

        await ctx.author.send(codeblock)

        await ctx.send(
            'Successfully opened the currently loaded config and sent it to you !'
        )
Ejemplo n.º 17
0
    async def endings(self, ctx, index: int = 0):
        """Enables/disables an ending. See docs for details"""

        if not index:
            # Print out currently enabled endings
            message = loc["endings"]["EndingsEnabled"] + "\n"
            message += ", ".join(f"{end}" for end in ctx.game.endings
                                 if ctx.game.endings[end])

            message = utils.codeblock(message)
            asyncio.create_task(ctx.send(message))
        else:
            # Toggle specified ending
            ctx.game.endings[index] = not ctx.game.endings[index]
Ejemplo n.º 18
0
    async def source(self,
                     ctx: core.Context,
                     *,
                     target: CommandConverter = None):
        """Gets the source for a command"""
        if target is None:
            return await ctx.send(
                f"Drop a star to support my development !\n<{self.bot.config['github']['url']}>"
            )

        callback = target.callback

        if 'help' in target.name:  # special case
            callback = self.bot.help_command.__class__

        try:
            source_lines, line_number = inspect.getsourcelines(callback)

        except OSError:
            raise commands.BadArgument(
                "Sorry ! I couldn't retrieve this command's source code")

        source_lines = textwrap.dedent(''.join(source_lines))

        module = callback.__module__.replace('.', '/') + '.py'

        github_link = f"{self.bot.config['github']['url']}{GITHUB_PATH}{module}#L{line_number}"

        embed = utils.Embed(
            title=f"""Here's the source the command named "{target}" !""")

        embed.add_fields(('External view', f'[Github]({github_link})'),
                         ('Module', discord.utils.escape_markdown(module)),
                         ('Line', line_number))

        if len(source_lines) > 2000:

            content = "Sorry ! The source is too long so I can only send the external view"

            return await ctx.send(content=content, embed=embed)

        src = utils.codeblock(source_lines, lang='py')
        await utils.OnePage({'embed': embed, 'content': src}).start(ctx)
Ejemplo n.º 19
0
class Meta(commands.Cog, name="meta"):
    """General and utility commands"""
    def __init__(self, bot):
        self.bot: utils.MyBot = bot
        self.show_name = "\N{ROBOT FACE} Meta"
        self.logger = utils.create_logger(self.__class__.__name__,
                                          logging.INFO)

        self.weather_api_key = self.bot.settings["keys"]["weather_api"]

    @asynccontextmanager
    async def google_search(self, query: str):
        """Context Manager to search for the CSE"""

        with contextlib.suppress(KeyError):
            keys = [
                self.bot.settings["keys"]["google_cse"],
                self.bot.settings["keys"]["second_google_cse"]
            ]
            results = []
            try:
                engine = cse.Search(keys[0])
                results = await engine.search(query,
                                              safe_search=True,
                                              max_results=10)
            except cse.QuotaExceededError:
                engine = cse.Search(keys[1])
                results = await engine.search(query,
                                              safe_search=True,
                                              max_results=10)
            finally:
                await engine.close()
                yield results

    def _get_top_coloured_role(self, target: discord.Member):
        """Helper method to get the users top role that has colour else no colour."""

        roles = target.roles
        roles.reverse()

        for role in roles:
            if role.colour != discord.Colour(0):
                return role.colour

        return discord.Colour(0)

    async def _get_task_by_enumeration(self, user: discord.Member,
                                       enumerated_id: int):
        """Helper method to get a task by its enumeration ID."""

        sql = "SELECT * FROM todos WHERE user_id = $1"
        all_tasks = await self.bot.pool.fetch(sql, user.id)

        try:
            resultant_task = all_tasks[enumerated_id - 1]
        except IndexError:
            raise commands.BadArgument(
                "You do not have a task that corresponds with that ID.")

        return resultant_task["id"]

    @commands.command(aliases=["readthefuckingsource"])
    async def rtfs(self, ctx: utils.CustomContext, *, query: t.Optional[str]):
        """Queries the discord.py source with a given search."""

        if not query:
            return await ctx.send(
                "You didn't provide a query, "
                "here's the link to the source. <https://github.com/Rapptz/discord.py/>"
            )

        params = {
            "query": query,
            "library": "discord.py",
        }

        url = "https://idevision.net/api/public/rtfs"
        async with self.bot.session.get(url, params=params) as resp:
            if resp.status != 200:
                return await ctx.send(
                    f"The API returned a {resp.status} status.")

            data = await resp.json()
            embed = self.bot.embed(ctx)

            description = []

            for node_name, url_source in data["nodes"].items():
                description.append(f"[`{node_name}`]({url_source})")

            embed.description = "\n".join(description)

            await ctx.send(embed=embed)

    async def _get_embed_for_manual(self, ctx: utils.CustomContext,
                                    location: str, query: str):
        params = {
            "query": query,
            "location": location,
            "show-labels": "true" if query.startswith("label:") else "false",
            "label-labels": "true",
        }

        url = "https://idevision.net/api/public/rtfm"
        async with self.bot.session.get(url, params=params) as resp:
            if resp.status != 200:
                raise commands.BadArgument("Couldn't find that, sorry!")

            data = await resp.json()
            embed = self.bot.embed(ctx)

            description = []

            for node_name, url_source in data["nodes"].items():
                description.append(f"[`{node_name}`]({url_source})")

            embed.description = "\n".join(description)
            return embed

    @commands.group(
        aliases=["rtfd", "readthefuckingdocs", "readthefuckingmanual"],
        invoke_without_command=True)
    async def rtfm(self, ctx: utils.CustomContext, *, query: str):
        """Queries the discord.py documentation with a given search."""

        embed = await self._get_embed_for_manual(
            ctx, "https://discordpy.readthedocs.io/en/stable/", query)
        await ctx.send(embed=embed)

    @rtfm.command(name="pil", aliases=["pillow"])
    async def rtfm_pil(self, ctx: utils.CustomContext, *, query: str):
        """Queries the Pillow documentation for a given search."""

        embed = await self._get_embed_for_manual(
            ctx, "https://pillow.readthedocs.io/en/stable/", query)
        await ctx.send(embed=embed)

    @rtfm.command(name="asyncpg", aliases=["apg"])
    async def rtfm_asyncpg(self, ctx: utils.CustomContext, *, query: str):
        """Queries the Asyncpg documentation for a given search."""

        embed = await self._get_embed_for_manual(
            ctx, "https://magicstack.github.io/asyncpg/current/", query)
        await ctx.send(embed=embed)

    @rtfm.command(name="aiohttp", aliases=["http"])
    async def rtfm_aiohttp(self, ctx: utils.CustomContext, *, query: str):
        """Queries the aiohttp documentation for a given search."""

        embed = await self._get_embed_for_manual(
            ctx, "https://docs.aiohttp.org/en/stable/", query)
        await ctx.send(embed=embed)  # https://docs.wand-py.org/en/0.6.5/

    @rtfm.command(name="wand")
    async def rtfm_wand(self, ctx: utils.CustomContext, *, query: str):
        """Queries the wand documentation for a given search."""

        embed = await self._get_embed_for_manual(
            ctx, "https://docs.wand-py.org/en/0.6.5/", query)
        await ctx.send(embed=embed)

    @commands.command(name="run")
    async def _run(self, ctx: utils.CustomContext, *,
                   code: codeblock_converter):
        """Runs a given piece of code.
        Use the given syntax modifier in the codeblock to determine the language."""

        short_hand = {
            "py": "python",
            "c++": "cpp",
            "ts": "typescript",
            "js": "javascript",
        }

        content = code.content
        url = "https://emkc.org/api/v1/piston/execute"

        if not code.language:
            raise commands.BadArgument(
                "You must provide a codeblock with a certain language.")

        lang = code.language.lower()

        if (new_lang := short_hand.get(lang, None)):
            lang = new_lang

        if lang.lower() == "java":
            lines = code.content.splitlines()
            base = [
                "public class temp extends Object {public static void main(String[] args) {"
            ]
            for line in lines:
                base.append(line)
            base.append("}}")

            content = "\n".join(base)

        data = {
            "language": lang,
            "source": content,
            "args": [],
        }

        async with ctx.typing():
            async with self.bot.session.post(url, json=data) as res:
                res_data = await res.json()
                if res.status != 200:
                    return await ctx.send(
                        f"{res.status} - {res_data['message']}")

                embed = self.bot.embed(ctx)

                if (stdout := res_data["stdout"]):
                    value = stdout if len(
                        stdout) < 1500 else await utils.mystbin(
                            self.bot.session, stdout)
                    embed.add_field(name="Stdout",
                                    value=utils.codeblock(
                                        value, language=code.language))

                if (stderr := res_data["stderr"]):
                    value = stderr if len(
                        stderr) < 1500 else await utils.mystbin(
                            self.bot.session, stderr)
                    embed.add_field(name="Stderr",
                                    value=utils.codeblock(
                                        value, language=code.language))

                await ctx.send(embed=embed)
Ejemplo n.º 20
0
    async def init(self, ctx):
        """Initial setup before character selection"""

        if ctx.game.start_time:
            await ctx.send("Game has already begun!")
            return
        elif ctx.game.init and ctx.game.automatic:
            # Disallow unless manual mode is enabled
            await ctx.send("Initialization already run!")
            return

        await ctx.send("Initializing game")

        # Introduction images
        utils.send_image("player-resources", utils.MASTER_PATHS["guide"], ctx)
        utils.send_image("player-resources",
                         utils.MASTER_PATHS["character_sheet"], ctx)
        utils.send_image("player-resources", utils.MASTER_PATHS["intro"], ctx)

        ctx.game.alice = random.randint(1, 10)
        alice = utils.POSTER_DIR / ("Alice Briarwood " + str(ctx.game.alice) +
                                    utils.IMAGE_EXT)
        utils.send_image("player-resources", alice, ctx)

        # Send characters, suspects, and locations to appropriate channels
        utils.send_folder("character-cards", utils.CHARACTER_IMAGE_DIR, ctx)
        utils.send_folder("suspect-cards", utils.SUSPECT_IMAGE_DIR, ctx)
        utils.send_folder("location-cards", utils.LOCATION_IMAGE_DIR, ctx)

        # Instructions for Charlie Barnes
        channel = ctx.text_channels["charlie-clues"]
        prompts = "\n".join([
            "Read introduction",
            "Introduce Alice from poster",
            "Introduce/pick characters",
            "Explain character cards",
            "Explain motive cards",
            "Character backgrounds",
            "Relationships",
            "Suspects and locations",
            "!setup_clues",
            "Explain clue cards",
            "Explain searching",
            "Game guide",
            "Voicemails",
            "Stream timer (https://www.youtube.com/watch?v=ysOOFIOAy7A)",
            "!start",
            "Send first message",
        ])
        prompts = utils.codeblock(prompts)

        background = " ".join([
            "CHARLIE BARNES moved away from Silent Falls",
            "with their mom at the end of the last school year",
            "after their parents divorced. They just arrived in",
            "town to stay with their dad for winter break, and",
            "hope to see Alice and the others while they’re here.",
            "A few days ago, Alice stopped responding.",
            "They haven’t heard from her since.",
        ])

        background = utils.codeblock(background)

        asyncio.create_task(channel.send(prompts))
        asyncio.create_task(channel.send(background))

        # Character and motive cards in clues channels
        for name in gamedata.CHARACTERS:
            channel = ctx.text_channels[f"{name}-clues"]
            utils.send_image(channel, utils.MASTER_PATHS[name], ctx)

        # Shuffle and send motives if in automatic mode
        if ctx.game.automatic:
            await self.bot.cogs["Manual"].shuffle_motives(ctx)
            asyncio.create_task(self.bot.cogs["Manual"].send_motives(ctx))

        ctx.game.init = True