async def search(self, ctx: context.CustomContext, *, query: str):
        """Search for a global or local tag on this server"""

        db_query = """SELECT tag.name, tag.title FROM tag
                      JOIN tag_lookup l on l.tag_id = tag.id
                      WHERE (tag.global = true OR tag.guild_id = $2)
                      AND (lower(l.alias) % $1 OR lower(l.alias) LIKE '%' || $1 || '%' OR lower(tag.title) LIKE '%' || $1 || '%' 
                          OR lower(tag.content) LIKE '%' || $1 || '%')
                      ORDER BY similarity(l.alias, $1) DESC
                      LIMIT 20
                    """

        guild_id = 0 if not ctx.guild else ctx.guild.id
        icon = self.bot.user.display_avatar.url if not ctx.guild else ctx.guild_icon
        tags = await self.bot.db.fetch(db_query, query.lower(), guild_id)
        pretty_names = (
            {}
        )  # Abuse dict as ordered set since you can't use DISTINCT in above SQL query

        for record in tags:
            pretty_names[
                f"`{config.BOT_PREFIX}{record['name']}`  {record['title']}"
            ] = None

        pages = paginator.SimplePages(
            entries=list(pretty_names),
            author=f"Tags matching '{query}'",
            icon=icon,
            empty_message="Nothing found.",
            per_page=12,
        )

        await pages.start(ctx)
Example #2
0
    async def _list_webhooks(
        self,
        ctx,
        *,
        endpoint: str,
        webhook_name: str,
        command_name: str,
        icon: str,
        fmt: typing.Callable,
    ):
        webhooks = await self.bot.api_request("GET",
                                              f"{endpoint}{ctx.guild.id}")
        entries = []

        for webhook in webhooks["webhooks"]:
            try:
                discord_webhook = await self.bot.fetch_webhook(
                    webhook["webhook_id"])
            except discord.HTTPException:
                continue

            entries.append(fmt(webhook, discord_webhook))

        pages = paginator.SimplePages(
            author=f"{webhook_name.title()} on {ctx.guild.name}",
            icon=icon,
            entries=entries,
            per_page=12,
            empty_message=
            f"This server does not have any {webhook_name} yet.\n\nAdd some "
            f"with `{config.BOT_PREFIX}server {command_name} add`.",
        )
        await pages.start(ctx)
Example #3
0
    async def banana(self, ctx):
        """List all bank accounts with the Banana Coin currency"""

        response = await self.request(BankRoute("GET",
                                                "accounts/currency/BNC"))
        json = await response.json()
        desc = []
        accounts_by_person = collections.defaultdict(list)

        for account in json:
            fmt = (
                f"{account['iban']}: "
                f"`{self.get_currency(account['balance_currency']).with_amount(account['balance'])}`"
            )

            accounts_by_person[account["pretty_holder"]].append(fmt)

        for person, accounts in accounts_by_person.items():
            person = discord.utils.remove_markdown(person)
            fmt = "\n".join(accounts)
            desc.append(f"**__{person}__**\n{fmt}\n")

        pages = paginator.SimplePages(
            entries=desc,
            icon=self.bot.mk.NATION_ICON_URL or self.bot.dciv.icon.url,
            author=f"All Bank Accounts with Banana Coin ({len(json)})",
        )
        await pages.start(ctx)
Example #4
0
    async def _paginate_all_(self, ctx, *, model):
        per_page = None

        if model is models.Bill:
            all_objects = await self.bot.db.fetch("SELECT id FROM bill ORDER BY id;")
        elif model is models.Law:
            all_objects = await self.bot.db.fetch(
                "SELECT id FROM bill WHERE status = $1 ORDER BY id;",
                models.BillIsLaw.flag.value,
            )
        elif model is models.Motion:
            per_page = 12
            all_objects = await self.bot.db.fetch("SELECT id FROM motion ORDER BY id;")

        formatted = []

        for record in all_objects:
            obj = await model.convert(ctx, record["id"])
            formatted.append(obj.formatted)

        if model is models.Law:
            title = f"All Laws in {self.bot.mk.NATION_NAME}"
            empty_message = f"There are no laws yet."
        else:
            title = f"All Submitted {model.__name__}s"
            empty_message = f"No one has submitted any {model.__name__.lower()}s yet."

        pages = paginator.SimplePages(
            entries=formatted,
            icon=self.bot.mk.NATION_ICON_URL,
            author=title,
            empty_message=empty_message,
            per_page=per_page,
        )
        await pages.start(ctx)
Example #5
0
    async def list_npcs(
        self,
        ctx: CustomContext,
        *,
        person: Fuzzy[
            converter.CaseInsensitiveMember,
            converter.CaseInsensitiveUser,
            FuzzySettings(weights=(5, 1)),
        ] = None,
    ):
        """List every NPC you or someone else has access to

        **Example**
           `{PREFIX}{COMMAND}` list all of your NPCs
           `{PREFIX}{COMMAND} @DerJonas` to see what NPCs someone else has access to
           `{PREFIX}{COMMAND} DerJonas#8036`
           `{PREFIX}{COMMAND} Jonas`"""

        member = person or ctx.author
        npcs = [self._npc_cache[i] for i in self._npc_access_cache[member.id]]
        npcs.sort(key=lambda npc: npc["id"])

        pretty_npcs = []

        for record in npcs:
            avatar = (
                f"[Avatar]({record['avatar_url']})\n" if record["avatar_url"] else ""
            )

            owner = self.bot.get_user(record["owner_id"])
            owner_value = (
                "\n"
                if not owner
                else f"Owner: {owner.mention} {escape_markdown(str(owner))}\n"
            )

            pretty_npcs.append(
                f"**__NPC #{record['id']} - {escape_markdown(record['name'])}__**"
            )
            pretty_npcs.append(
                f"{avatar}Trigger Phrase: `{escape_markdown(record['trigger_phrase'])}`"
            )
            pretty_npcs.append(owner_value)

        if pretty_npcs:
            pretty_npcs.insert(
                0,
                f"You can create a new NPC with `{config.BOT_PREFIX}npc create`, "
                f"or edit the name, avatar and/or trigger phrase of an existing one with "
                f"`{config.BOT_PREFIX}npc edit <npc>`.\n",
            )

        pages = paginator.SimplePages(
            author=f"{member.display_name}'s NPCs",
            icon=member.display_avatar.url,
            entries=pretty_npcs,
            per_page=20,
            empty_message="This person hasn't made any NPCs yet.",
        )
        await pages.start(ctx)
    async def _from(
        self,
        ctx: context.CustomContext,
        *,
        person: Fuzzy[
            CaseInsensitiveMember, CaseInsensitiveUser, FuzzySettings(weights=(5, 1))
        ] = None,
    ):
        """List the tags that someone made on this server"""

        member = person or ctx.author
        all_tags = await self.bot.db.fetch(
            "SELECT * FROM tag WHERE author = $1 AND guild_id = $2 ORDER BY uses desc",
            member.id,
            ctx.guild.id,
        )

        pretty_tags = []

        for record in all_tags:
            pretty_tags.append(
                f"`{config.BOT_PREFIX}{record['name']}`  {escape_markdown(record['title'])}"
            )

        pages = paginator.SimplePages(
            entries=pretty_tags,
            author=f"Tags from {member.display_name}",
            icon=member.display_avatar.url,
            empty_message=f"{member} hasn't made any tags on this server yet.",
            per_page=12,
        )
        await pages.start(ctx)
Example #7
0
    async def _from_person_model(self, ctx, *, member_or_party, model, paginate=True):
        member = member_or_party or ctx.author
        submit_term = "written" if model is models.Law else "submitted"
        per_page = None

        if isinstance(member, converter.PoliticalParty):
            name = member.role.name
            members = [m.id for m in member.role.members]
            empty = (
                f"No member of {name} has {submit_term} a {model.__name__.lower()} yet."
            )
            title = f"{model.__name__}s from members of {name}"
            icon = await member.get_logo() or self.bot.mk.NATION_ICON_URL or EmptyEmbed
        else:
            name = member.display_name
            members = [member.id]
            empty = f"{name} hasn't {submit_term} any {model.__name__.lower()}s yet."
            title = f"{model.__name__}s from {name}"
            icon = member.display_avatar.url

        if model is models.Bill:
            objs_from_thing = await self.bot.db.fetch(
                "SELECT id FROM bill WHERE submitter = ANY($1::bigint[]) ORDER BY id;",
                members,
            )

        elif model is models.Law:
            objs_from_thing = await self.bot.db.fetch(
                "SELECT id FROM bill WHERE submitter = ANY($1::bigint[]) AND status = $2 ORDER BY id;",
                members,
                models.BillIsLaw.flag.value,
            )
        else:
            objs_from_thing = await self.bot.db.fetch(
                "SELECT id FROM motion WHERE submitter = ANY($1::bigint[]) ORDER BY id;",
                members,
            )
            per_page = 12

        formatted = []

        for record in objs_from_thing:
            obj = await model.convert(ctx, record["id"])
            formatted.append(obj.formatted)

        if not paginate:
            return formatted

        pages = paginator.SimplePages(
            entries=formatted,
            author=title,
            icon=icon,
            per_page=per_page,
            empty_message=empty,
        )
        await pages.start(ctx)
Example #8
0
    async def search(self, ctx, *, query: str):
        """Search for laws"""
        results = await self._search_model(ctx, model=models.Law, query=query)

        pages = paginator.SimplePages(
            entries=results,
            icon=self.bot.mk.NATION_ICON_URL,
            author=f"Laws matching '{query}'",
            empty_message="Nothing found.",
        )
        await pages.start(ctx)
    async def lyrics(self, ctx, *, query: str):
        """Find lyrics for a song

        **Usage**
            `{PREFIX}{COMMAND} <query>` to search for lyrics that match your query
        """

        if len(query) < 3:
            return await ctx.send(
                f"{config.NO} The query to search for has to be more than 3 characters!"
            )

        async with ctx.typing():
            async with self.bot.session.get(
                    f"https://some-random-api.ml/lyrics?title={query}"
            ) as response:
                if response.status == 200:
                    lyrics = await response.json()
                else:
                    return await ctx.send(
                        f"{config.NO} Genius could not suggest me anything related to `{query}`."
                    )

        try:
            lyrics["lyrics"] = lyrics["lyrics"].replace("[", "**[").replace(
                "]", "]**")

            if len(lyrics["lyrics"]) <= 2048:
                embed = text.SafeEmbed(
                    title=lyrics["title"],
                    description=lyrics["lyrics"],
                    colour=0x2F3136,
                    url=lyrics["links"]["genius"],
                )
                embed.set_author(name=lyrics["author"])
                embed.set_thumbnail(url=lyrics["thumbnail"]["genius"])
                return await ctx.send(embed=embed)

            pages = paginator.SimplePages(
                entries=lyrics["lyrics"].splitlines(),
                title=lyrics["title"],
                title_url=lyrics["links"]["genius"],
                author=lyrics["author"],
                thumbnail=lyrics["thumbnail"]["genius"],
                colour=0x2F3136,
            )

        except KeyError:
            return await ctx.send(
                f"{config.NO} Genius could not suggest me anything related to `{query}`."
            )

        await pages.start(ctx)
Example #10
0
    async def automatic(self, ctx: CustomContext):
        """Automatically write as an NPC in a specific channel or channel category without having to use its trigger phrase"""
        automatic_channel = await self.bot.db.fetch(
            "SELECT npc_automatic_mode.npc_id, npc_automatic_mode.channel_id FROM npc_automatic_mode "
            "WHERE npc_automatic_mode.user_id = $1 "
            "AND npc_automatic_mode.guild_id = $2",
            ctx.author.id,
            ctx.guild.id,
        )

        grouped_by_npc = collections.defaultdict(list)
        pretty = [
            f"If you want to automatically speak as an NPC in a certain channel or channel category "
            f"without having to use the trigger phrase, use `{config.BOT_PREFIX}npc automatic "
            f"on <npc>`, or disable it with "
            f"`{config.BOT_PREFIX}npc automatic off <npc>`.\n\nYou can only have one "
            f"automatic NPC per channel.\n\nIf you have one NPC as automatic in an entire category, "
            f"but a different NPC in a single channel that is that same category, and you write "
            f"something in that channel, you will only speak as the NPC for that "
            f"specific channel, and not as both NPCs.\n\n"
        ]

        for record in automatic_channel:
            grouped_by_npc[record["npc_id"]].append(
                ctx.guild.get_channel(record["channel_id"])
            )

        for k, v in grouped_by_npc.items():
            npc = self._npc_cache[k]
            pretty_chan = [
                f"- {c.mention if type(c) is discord.TextChannel else f'{c.name} Category'}"
                for c in v
            ]
            pretty_chan = "\n".join(pretty_chan)
            pretty.append(f"**__{npc['name']}__**\n{pretty_chan}\n")

        if len(pretty) > 1:
            pages = paginator.SimplePages(
                entries=pretty,
                icon=ctx.guild_icon,
                per_page=15,
                author=f"{ctx.author.display_name}'s Automatic NPCs",
            )
            await pages.start(ctx)

        else:
            embed = text.SafeEmbed(description=pretty[0])
            embed.set_author(
                name=f"{ctx.author.display_name}'s Automatic NPCs",
                icon_url=ctx.guild_icon,
            )
            await ctx.send(embed=embed)
Example #11
0
    async def match(self, ctx, *, query):
        """Extract information from bills, laws & motions with Natural Language Processing:tm:

        This is an experimental command and probably still a work-in-progress."""

        if self.bot.mk.IS_MULTICIV:
            return await ctx.send(
                f"{config.NO} This command is disabled during Multiciv MKs.")

        async with ctx.typing():
            response = await self.bot.api_request("POST",
                                                  "ml/information_extraction",
                                                  json={"question": query})

        if not response:
            return await ctx.send(
                f"{config.NO} I couldn't find anything that matches `{query}`. Sorry!"
            )

        fmt = [
            "This uses Natural Language Processing to topic match your query against all existing bills & motions "
            "and shows the most closely corresponding excerpts. Using full, grammatical expressions with "
            f"no spelling errors will improve the quality of your results.\n\nShould this feature come out of "
            f"beta, then it will probably be integrated into the `{config.BOT_PREFIX}laws search` and "
            f"`{config.BOT_PREFIX}legislature bills search` commands.\n\n"
        ]

        for result in response:
            thing, thing_id = result["document_label"].split("_")

            if thing == "bill":
                obj = await models.Bill.convert(ctx, thing_id)

            elif thing == "motion":
                obj = await models.Motion.convert(ctx, thing_id)

            else:
                continue

            txt = discord.utils.escape_markdown(result["text"])
            fmt.append(f"**__{obj.formatted}__**")
            fmt.append(f"```{txt}```\n")

        pages = paginator.SimplePages(
            entries=fmt,
            icon=self.bot.mk.NATION_ICON_URL,
            author=f"[BETA] Results for '{query}'",
        )

        await pages.start(ctx)
Example #12
0
    async def _show_bill_text(self, ctx, bill: models.Bill, *, ephemeral_webhook=None):
        entries = bill.content.splitlines()
        entries.insert(
            0,
            f"[Link to the Google Docs document of this Bill]({bill.link})\n"
            f"*Am I showing you outdated or wrong text? Tell the {self.bot.mk.speaker_term} to "
            f"synchronize this text with the Google Docs text of this bill with "
            f"`{config.BOT_PREFIX}{self.bot.mk.LEGISLATURE_COMMAND} bill synchronize {bill.id}`.*\n\n",
        )
        pages = paginator.SimplePages(
            entries=entries,
            icon=self.bot.mk.NATION_ICON_URL,
            author=f"{bill.name} (#{bill.id})",
            ephemeral_webhook=ephemeral_webhook,
        )

        await pages.start(ctx)
Example #13
0
    async def nationroles(self, ctx):
        """List all nation-specific roles that can be given out with `{PREFIX}nation roles toggle`"""

        predicate = lambda r: r.name.lower().startswith(
            self.bot.mk.NATION_ROLE_PREFIX.lower())
        found = filter(predicate, ctx.guild.roles)
        fmt = [r.mention for r in found]
        fmt.insert(
            0,
            f"These roles can be given out with `{config.BOT_PREFIX}nation roles toggle` by you.\n",
        )

        pages = paginator.SimplePages(
            entries=fmt,
            author=f"Nation Roles",
            icon=self.bot.mk.safe_flag,
            empty_message="There are no roles that you can give out.",
            per_page=12,
        )
        await pages.start(ctx)
    async def local(self, ctx: context.CustomContext):
        """List all non-global tags on this server"""

        all_tags = await self.bot.db.fetch(
            "SELECT * FROM tag WHERE guild_id = $1 AND global = false "
            "ORDER BY uses desc",
            ctx.guild.id,
        )
        pretty_tags = []

        for record in all_tags:
            pretty_tags.append(
                f"`{config.BOT_PREFIX}{record['name']}`  {escape_markdown(record['title'])}"
            )

        pages = paginator.SimplePages(
            entries=pretty_tags,
            author=f"Local Tags in {ctx.guild.name}",
            icon=ctx.guild_icon,
            empty_message="There are no local tags on this server.",
            per_page=12,
        )
        await pages.start(ctx)
Example #15
0
    async def exclude(
        self,
        ctx,
        *,
        channel: Fuzzy[converter.CaseInsensitiveTextChannel,
                       converter.CaseInsensitiveCategoryChannel, ] = None,
    ):
        """Hide a channel or category from your server's log channel

        Both text channels and entire categories can be hidden.

        Threads inherit whether they are hidden from the channel they belong to.

         **Usage**
             `{PREFIX}{COMMAND}` to see all hidden channels/categories
             `{PREFIX}{COMMAND} <channel>` to hide or unhide a channel or category
        """
        channel: typing.Union[discord.TextChannel, discord.CategoryChannel]
        settings = await self.ensure_guild_settings(ctx.guild.id)
        current_logging_channel = await self.bot.get_logging_channel(ctx.guild)

        if current_logging_channel is None:
            return await ctx.send(
                f"{config.NO} This server currently has no logging channel. "
                f"Please set one with `{config.BOT_PREFIX}server logs`.")

        help_description = (
            f"When you hide a channel, it (and all its threads) will no longer show up in {current_logging_channel.mention}.\n\nAdditionally,"
            f" :star: reactions for the starboard will no longer count in that channel (and in all its threads).\n\nYou can hide a channel, "
            f"or even an entire category at once, with "
            f"`{config.BOT_PREFIX}server hidechannel <channel_name>`\n\n__**Hidden Channels**__"
        )

        private_channels = settings["private_channels"]

        if not channel:
            current_excluded_channels_by_name = [help_description]

            if not private_channels:
                return await ctx.send(
                    f"{config.NO} There are no hidden channels on this server yet. "
                    f"You can hide a channel so that it no longer shows up in "
                    f"{current_logging_channel.mention} with "
                    f"`{config.BOT_PREFIX}server hidechannel <channel_name>`."
                    f"\n{config.HINT} You can also hide entire categories! Just hide the category "
                    f"and every channel in that category will be hidden automatically. "
                    f"Note that if channel is hidden, :star: reactions for the starboard will no "
                    f"longer count in it.")

            for channel_id in private_channels:
                channel = self.bot.get_channel(channel_id)
                if channel:
                    current_excluded_channels_by_name.append(channel.mention)

            pages = paginator.SimplePages(
                entries=current_excluded_channels_by_name,
                author=f"Hidden Channels on {ctx.guild.name}",
                icon=ctx.guild_icon,
                empty_message="There are no hidden channels on this server.",
            )
            return await pages.start(ctx)

        else:
            is_category = isinstance(channel, discord.CategoryChannel)

            # Remove channel
            if channel.id in private_channels:
                await self.bot.db.execute(
                    "DELETE FROM guild_private_channel WHERE guild_id = $1 AND channel_id = $2",
                    ctx.guild.id,
                    channel.id,
                )
                await self.bot.update_guild_config_cache()

                if is_category:
                    star = (
                        f"\n{config.HINT} *Note that :star: reactions for the starboard will now count again in every one of these channels and their threads.*"
                        if ctx.guild.id == self.bot.dciv.id
                        and config.STARBOARD_ENABLED else "")
                    await ctx.send(
                        f"{config.YES} The {channel} category **is no longer hidden**, and all channels in it and their threads "
                        f"will show up in {current_logging_channel.mention} again.{star}"
                    )
                else:
                    star = (
                        f"\n{config.HINT} *Note that :star: reactions for the starboard will now count again in this channel and in all its threads.*"
                        if ctx.guild.id == self.bot.dciv.id
                        and config.STARBOARD_ENABLED else "")
                    await ctx.send(
                        f"{config.YES} {channel.mention} **is no longer hidden**, "
                        f"and it and all its threads will show up in {current_logging_channel.mention} again.{star}"
                    )

            # Add channel
            elif channel.id not in private_channels:
                await self.bot.db.execute(
                    "INSERT INTO guild_private_channel (guild_id, channel_id) VALUES ($1, $2)",
                    ctx.guild.id,
                    channel.id,
                )

                if is_category:
                    star = (
                        f"\n{config.HINT} *Note that :star: reactions for the starboard will also no longer count in any of these channels and their threads.*"
                        if ctx.guild.id == self.bot.dciv.id
                        and config.STARBOARD_ENABLED else "")

                    await ctx.send(
                        f"{config.YES} The {channel} category **is now hidden**, and all the channels in it and their threads "
                        f"will no longer show up in {current_logging_channel.mention}.{star}"
                    )
                else:
                    star = (
                        f"\n{config.HINT} *Note that :star: reactions for the starboard will also no longer count in this channel and its threads.*"
                        if ctx.guild.id == self.bot.dciv.id
                        and config.STARBOARD_ENABLED else "")

                    await ctx.send(
                        f"{config.YES} {channel.mention} (and all its threads) **are now hidden** and will no longer show up "
                        f"in {current_logging_channel.mention}.{star}")
                await self.bot.update_guild_config_cache()
Example #16
0
    async def accounts(self, ctx):
        """See the balance of every bank account you have access to"""
        await self.is_connected_with_bank_user(ctx)

        if ctx.guild:
            embed = text.SafeEmbed(
                title=f"{config.HINT}  Privacy Prompt",
                description="Are you sure that you want to proceed?\n\n"
                "Everyone in this channel would be able to see the balance of all bank accounts "
                "you have access to.\n\n*Using this command in DMs with me does not "
                "trigger this question.*",
            )
            privacy_q = await ctx.send(embed=embed)
            reaction = await ctx.confirm(message=privacy_q)

            await privacy_q.delete()

            if not reaction:
                return

        response = await self.request(
            BankRoute("GET", f"accounts/{ctx.author.id}/"))
        if response.status != 200:
            raise BankError(
                f"{config.NO} {self.bot.owner.mention}, something went wrong!")

        json = await response.json()
        desc = ["\n"]
        total_per_currency = {}

        for account in json:
            if ctx.guild:
                name = "__*Bank Account Name Censored - Use command in DMs to reveal*__"
            else:
                if not account["corporate_holder"]:
                    name = f"__**{account['name']}**__"
                else:
                    name = f"__**{account['corporate_holder']['name']}: {account['name']}**__"

            amount = self.get_currency(
                account["balance_currency"]).with_amount(account["balance"])

            desc.append(f"{name}\n*{account['iban']}*\n```diff\n+ {amount}```")

            if not account["is_reserve"]:
                try:
                    total_per_currency[
                        account["pretty_balance_currency"]] += decimal.Decimal(
                            account["balance"])
                except KeyError:
                    total_per_currency[
                        account["pretty_balance_currency"]] = decimal.Decimal(
                            account["balance"])

        prepend_desc = [
            "**Total Balance per Currency**\n*Reserve accounts aren't counted*"
        ]
        for cur, amount in total_per_currency.items():
            prepend_desc.append(f"{cur}: {amount}")

        desc.append("\n".join(prepend_desc))

        pages = paginator.SimplePages(
            entries=desc,
            author=f"{ctx.author.display_name}'s Bank Accounts",
            icon=ctx.author_icon,
            per_page=12,
        )
        await pages.start(ctx)
Example #17
0
    async def ask(self,
                  ctx,
                  batch_size: typing.Optional[int] = 1,
                  *,
                  question):
        """Get answers to a legal question with Deep (Machine) Learning:tm: and Neural Networks:tm:

        This is an experimental command and probably still a work-in-progress."""

        if self.bot.mk.IS_MULTICIV:
            return await ctx.send(
                f"{config.NO} This command is disabled during Multiciv MKs.")

        wait = await ctx.send(
            f"{config.HINT} This might take 30 to 60 seconds. Should this feature make it out "
            f"of beta, the time it takes will *hopefully* be sped up to just a couple of seconds by "
            f"switching to more powerful server hardware.\n:arrows_counterclockwise: Thinking "
            f"really hard about your question...")

        async with ctx.typing():
            start = time.time()
            response = await self.bot.api_request(
                "POST",
                "ml/question_answering",
                json={
                    "question": question,
                    "batch_size": batch_size
                },
            )
            end = time.time()

        duration = end - start

        await wait.delete()

        if not response:
            return await ctx.send(
                f"{config.NO} I couldn't find an answer to that question. Sorry!"
            )

        fmt = [
            "This uses deep learning, a particular machine learning method, with neural networks to try to "
            "find answers to a legal question you're asking. All currently existing bills & motions are taken into "
            "account to try to find the best answer. Google's BERT model in combination with Tensorflow Keras "
            "are used here.\n\nThis comes with no guarantees about the correctness of the answers. Do not expect "
            "this to be free of wrong, misleading or irrelevant answers.\n\n"
        ]

        for result in response:
            if result["score"] * 100 <= 1:
                continue

            if len(result["full_answer"]) - len(result["answer"]) <= 3:
                cntxt = []
            else:
                cntxt = [
                    f"__Context__",
                    f"```{discord.utils.escape_markdown(result['full_answer'])}```",
                ]

            thing, thing_id = result["document"].split("_")

            if thing == "bill":
                obj = await models.Bill.convert(ctx, thing_id)

            elif thing == "motion":
                obj = await models.Motion.convert(ctx, thing_id)

            else:
                continue

            fmt.append(
                f"**__Found answer in {obj.formatted} with a confidence of {result['score'] * 100:.2f}%__**"
            )
            fmt.append(
                f"```{discord.utils.escape_markdown(result['answer'])}```")
            fmt.extend(cntxt)

        pages = paginator.SimplePages(
            entries=fmt,
            author=f"[BETA] Results for '{question}'",
            icon=self.bot.mk.NATION_ICON_URL,
            reply=True,
        )

        await ctx.reply(
            f"{config.HINT} Keep in mind that this feature is still a work-in-progress in Beta. "
            f"Question answering took ~{duration:.2f} seconds with a batch size of {batch_size}."
        )
        await pages.start(ctx)
    async def tags(self, ctx: context.CustomContext, *, tag: Fuzzy[Tag] = None):
        """Access a tag or list all tags on this server

        **Example**
            `{PREFIX}{COMMAND}` to get a list of all tags on this server
            `{PREFIX}{COMMAND} constitution` to see the {PREFIX}constitution tag"""

        if tag:
            tag_content_type = self.get_tag_content_type(tag.content)

            if tag.is_embedded:
                if tag_content_type is TagContentType.IMAGE:
                    # invisible colour=0x2F3136
                    embed = text.SafeEmbed(title=tag.title)
                    embed.set_image(url=tag.content)

                    try:
                        return await ctx.send(embed=embed)
                    except discord.HTTPException:
                        return await ctx.send(tag.clean_content)
                elif tag_content_type is TagContentType.VIDEO:
                    # discord doesn't allow videos in embeds
                    return await ctx.send(tag.clean_content)

                embed = text.SafeEmbed(title=tag.title, description=tag.content)
                return await ctx.send(embed=embed)

            else:
                return await ctx.send(tag.clean_content)

        global_tags = await self.bot.db.fetch(
            "SELECT * FROM tag WHERE global = true ORDER BY uses desc"
        )
        pretty_tags = []

        if global_tags:
            pretty_tags = [
                f"**__Global Tags__**\n*Tags can only be made global by {self.bot.dciv.name} "
                f"Moderation and Nation Admins. Global tags work in every server I am in, as well as in DMs with me.*\n"
            ]

        for record in global_tags:
            pretty_tags.append(
                f"`{config.BOT_PREFIX}{record['name']}`  {escape_markdown(record['title'])}"
            )

        if ctx.guild:
            all_tags = await self.bot.db.fetch(
                "SELECT * FROM tag WHERE guild_id = $1 AND global = false"
                " ORDER BY uses desc",
                ctx.guild.id,
            )
            if all_tags:
                pretty_tags.append(
                    f"\n\n**__Local Tags__**\n*Every Tag that was not explicitly made global by "
                    f"{self.bot.dciv.name} Moderation or a Nation Admin is a local tag, "
                    f"and only works in the server it was made in.*\n"
                )

            for record in all_tags:
                pretty_tags.append(
                    f"`{config.BOT_PREFIX}{record['name']}`  {escape_markdown(record['title'])}"
                )

            author = f"All Tags in {ctx.guild.name}"
            icon = ctx.guild_icon
            empty_message = "There are no tags on this server."
        else:
            author = "All Global Tags"
            icon = self.bot.user.display_avatar.url
            empty_message = "There are no global tags yet."

        if len(pretty_tags) < 2:
            pretty_tags = []

        pages = paginator.SimplePages(
            entries=pretty_tags,
            author=author,
            icon=icon,
            empty_message=empty_message,
            per_page=12,
        )
        await pages.start(ctx)