Example #1
0
    async def log_error(
        self,
        ctx,
        error,
        to_log_channel: bool = True,
        to_owner: bool = False,
        to_context: bool = False,
    ):
        if ctx.guild is None:
            return

        embed = text.SafeEmbed(title=f"{config.NO}  Command Error")

        embed.add_field(
            name="Error",
            value=f"```{error.__class__.__name__}: {error}```",
            inline=False,
        )
        embed.add_field(name="Channel", value=ctx.channel.mention, inline=True)
        embed.add_field(name="Context",
                        value=f"[Jump]({ctx.message.jump_url})",
                        inline=True)
        caused_by = (ctx.message.clean_content
                     if len(ctx.message.clean_content) < 500 else "*too long*")
        embed.add_field(name="Caused by", value=caused_by, inline=False)

        if to_context:
            local_embed = text.SafeEmbed(
                title=":warning:  Something went wrong",
                description=
                f"An unexpected error occurred while performing this command. "
                f"The developer has been notified. Sorry!\n\n"
                f"```{error.__class__.__name__}: {error}```",
            )
            await ctx.send(embed=local_embed)

        if to_log_channel:
            log_channel = await self.get_logging_channel(ctx.guild)

            if log_channel is not None and await self.get_guild_setting(
                    ctx.guild.id, "logging_enabled"):
                await log_channel.send(embed=embed)

        if to_owner:
            embed.add_field(name="Guild",
                            value=f"{ctx.guild.name} ({ctx.guild.id})",
                            inline=False)
            pretty_traceback = "".join(
                traceback.format_exception(type(error), error,
                                           error.__traceback__))
            embed.add_field(name="Traceback",
                            value=f"```py\n{pretty_traceback}```")
            await self.owner.send(embed=embed)
Example #2
0
    async def on_guild_join(self, guild: discord.Guild):
        if len(self.guilds) >= 70:
            if guild.system_channel:
                embed = text.SafeEmbed(
                    title=":pensive:  Sorry!",
                    description=
                    "Discord requires bot developers to hand over their passport "
                    "once their bot reaches about ~75 servers. If they refuse to, "
                    f"the bot cannot be invited anymore and Discord takes away some "
                    f"critical features that the Democraciv Bot needs in order "
                    f"to work properly. I **do not want to give Discord my "
                    f"passport.**\n\nThis is the 70th server the bot is in, "
                    f"and as a precaution **the bot is leaving this server** again.",
                )

                await guild.system_channel.send(embed=embed)

            await guild.leave()
            return

        introduction_channel = guild.system_channel or guild.text_channels[0]

        # Alert owner of this bot that the bot was invited to some place
        await self.owner.send(
            f"{config.HINT} I was added to {guild.name} ({guild.id}).")
        p = config.BOT_PREFIX

        introduction_message = (
            f"You can check `{p}help` or `{p}commands` to get more "
            f"information about me.\nUse the `{p}server` command to configure me for this server."
            f"\n\n__**Icon Guide**__\n"
            f'{config.YES} - Confirm, Accept or "Success"\n'
            f'{config.NO} - Cancel, Decline or "Error"\n'
            f"{config.HINT} - Hint, Tip or additional information to explain something\n"
            f"{config.USER_INTERACTION_REQUIRED} - User reply is needed. You should reply with something, or confirm something"
            f"\n\nIf you have any questions or suggestions, send a DM to {self.owner.mention} ({self.owner})."
        )

        # Send introduction message to random guild channel
        embed = text.SafeEmbed(title="Thanks for inviting me!",
                               description=introduction_message)

        # Add new guild to database
        await self.db.execute(
            "INSERT INTO guild (id) VALUES ($1) ON CONFLICT DO NOTHING ",
            guild.id)
        await self.update_guild_config_cache()

        try:
            await introduction_channel.send(embed=embed)
        except discord.Forbidden:
            pass
    async def possible_alt_listener(self, member):
        if member.guild != self.bot.dciv:
            return

        if member.bot:
            return

        chance, details = await self.calculate_alt_chance(member)

        if chance >= 0.2:
            embed = text.SafeEmbed(title="Possible Alt Account Joined",
                                   description="")
            embed.add_field(name="Person",
                            value=f"{member.mention} ({member.id})",
                            inline=False)
            embed.add_field(
                name="Chance",
                value=
                f"There is a **{(chance * 100):.0f}%** chance that {member} is an alt.",
                inline=False,
            )
            if details:
                embed.add_field(name="Factors", value=details, inline=False)

            await self.bot.get_democraciv_channel(
                mk.DemocracivChannel.MODERATION_NOTIFICATIONS_CHANNEL
            ).send(embed=embed)
Example #4
0
    async def exportlaws(self, ctx: context.CustomContext):
        """Generate a Legal Code as a Google Docs document from the list of active laws in {NATION_NAME}"""

        doc_url = await ctx.input(
            f"{config.USER_INTERACTION_REQUIRED} Reply with an **edit** link to a Google Docs "
            "document you created. I will then fill that document to make it an up-to-date Legal Code.\n"
            ":warning: Note that I will replace the entire content of your Google Docs document if it "
            "isn't empty.",
            delete_after=True,
        )

        if not doc_url:
            ctx.command.reset_cooldown(ctx)
            return

        if not self.is_google_doc_link(doc_url):
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(
                f"{config.NO} That doesn't look like a Google Docs URL."
            )

        await ctx.send(
            f"{config.YES} I will generate an up-to-date Legal Code."
            f"\n:arrows_counterclockwise: This may take a few minutes..."
        )

        async with ctx.typing():
            all_laws = await self.bot.db.fetch(
                "SELECT id, name, link FROM bill WHERE status = $1 ORDER BY id;",
                models.BillIsLaw.flag.value,
            )
            ugly_laws = [dict(r) for r in all_laws]
            date = discord.utils.utcnow().strftime("%B %d, %Y at %H:%M")

            result = await self.bot.run_apps_script(
                script_id="MMV-pGVACMhaf_DjTn8jfEGqnXKElby-M",
                function="generate_legal_code",
                parameters=[
                    doc_url,
                    {"name": self.bot.mk.NATION_FULL_NAME, "date": date},
                    ugly_laws,
                ],
            )

        embed = text.SafeEmbed(
            title=f"Generated Legal Code",
            description="This Legal Code is not guaranteed to be correct. Its "
            f"content is based entirely on the list of Laws "
            f"in `{config.BOT_PREFIX}laws`."
            "\n\nRemember to change the edit link you "
            "gave me earlier to not be public.",
        )

        embed.add_field(
            name="Link to the Legal Code",
            value=result["response"]["result"]["view"],
            inline=False,
        )

        await ctx.send(embed=embed)
Example #5
0
    async def format_page(self, menu, cogs):
        prefix = config.BOT_PREFIX
        description = (
            f"Use `{prefix}help <thing>` for more info on a category or command."
        )

        embed = text.SafeEmbed(title="All Categories | Help", description=description)

        for cog in cogs:
            if cog.hidden:
                continue

            commands = self.commands.get(cog)
            if commands:
                # value = self.format_commands(cog, commands)

                if cog.description:
                    short_doc = cog.description.split("\n", 1)[0] + "\n"
                else:
                    short_doc = "No help found.\n"

                value = f"{short_doc}`{config.BOT_PREFIX}help {cog.qualified_name}`"
                embed.add_field(name=cog.qualified_name, value=value, inline=True)

        maximum = self.get_max_pages()
        embed.set_footer(text=f"Page {menu.current_page + 1}/{maximum}")
        return embed
    async def ping(self, ctx: context.CustomContext):
        """Pong!"""
        title = "Pong!" if ctx.invoked_with == "ping" else "Ping!"

        start = time.perf_counter()
        message = await ctx.send(":arrows_counterclockwise: Ping...")
        end = time.perf_counter()
        discord_http = (end - start) * 1000

        try:
            start = time.perf_counter()
            await self.bot.api_request("GET", "")
            end = time.perf_counter()
            api_http = (end - start) * 1000
        except exceptions.DemocracivBotAPIError:
            api_http = None

        embed = text.SafeEmbed(
            title=f":ping_pong:  {title}",
            description=
            "**[status.discord.com](https://status.discord.com/)**\n\n",
        )
        embed.add_field(
            name="Discord",
            value=
            f"HTTP API: {discord_http:.0f}ms\nGateway Websocket: {self.bot.ping}ms\n",
            inline=False,
        )

        embed.add_field(
            name="Internal API",
            value=f"{api_http:.0f}ms" if api_http else "*not running*",
        )
        await message.edit(content=None, embed=embed)
 async def addme(self, ctx):
     """Invite this bot to your Discord server"""
     invite_url = discord.utils.oauth_url(
         self.bot.user.id, permissions=discord.Permissions(8))
     await ctx.send(embed=text.SafeEmbed(
         title="Add this bot to your own Discord server",
         description=invite_url))
Example #8
0
    async def format_page(self, menu, commands):
        embed = text.SafeEmbed(title=self.title, description=self.description)

        for command in commands:
            try:
                is_allowed = await command.can_run(menu.ctx)
            except Exception:
                is_allowed = False

            if is_allowed:
                signature = f"__{config.BOT_PREFIX}{command.qualified_name} {command.signature}__"
            else:
                default_sig = f"__*{config.BOT_PREFIX}{command.qualified_name} {command.signature}"

                if default_sig.endswith(" "):
                    default_sig = default_sig[:-1]

                signature = f"{default_sig}*__"

            embed.add_field(
                name=signature,
                value=command.short_doc or "No help given.",
                inline=False,
            )

        maximum = self.get_max_pages()
        if maximum > 1:
            embed.set_footer(
                text=f"Page {menu.current_page + 1}/{maximum} ({len(self.entries)} commands)"
            )

        return embed
Example #9
0
    async def bank(self, ctx):
        """The Bank of Democraciv"""

        embed = text.SafeEmbed(
            description=
            f"The [{self.BANK_NAME}](https://democracivbank.com) provides the international community "
            f"with free financial "
            f"services: Personal & Shared Bank Accounts with complete transaction records, a "
            f"corporate registry, personalized notifications, support for multiple currencies and a "
            f"deep integration into the Democraciv Discord Bot.\n\nYou can register at "
            f"[democracivbank.com](https://democracivbank.com) and open as many bank accounts as you want. "
            f"We don't have, give out or loan any money ourselves, so to actually get some "
            f"money you need to consult with our third-party partners (usually governments) "
            f"that issue the currencies.\n\n"
            f"Connect your Discord Account on "
            f"[democracivbank.com/me/discord](https://democracivbank.com/me/discord) to get access to "
            f"the `{config.BOT_PREFIX}bank` commands, like "
            f"`{config.BOT_PREFIX}bank accounts` to view all of your balances right here in Discord, "
            f"and to enable personalized "
            f"notifications in real-time whenever someone sends you money.\n\nSee `{config.BOT_PREFIX}help "
            f"bank` for a list of all bank related commands here on Discord.")

        embed.set_author(name=self.BANK_NAME, icon_url=self.BANK_ICON_URL)
        embed.set_image(
            url=
            "https://cdn.discordapp.com/attachments/738903909535318086/837833943377903616/bank.PNG"
        )
        await ctx.send(embed=embed)
Example #10
0
    async def _log_npc_usage(
        self,
        npc,
        original_message: discord.Message,
        npc_message_url: str,
        npc_message_content: str,
    ):
        if not await self.bot.get_guild_setting(
            original_message.guild.id, "logging_enabled"
        ):
            return

        log_channel = await self.bot.get_logging_channel(original_message.guild)

        if not log_channel:
            return

        embed = text.SafeEmbed(title=":disguised_face:  NPC Used")
        embed.add_field(name="NPC", value=npc["name"], inline=False)
        embed.add_field(
            name="Real Author",
            value=f"{original_message.author.mention} {original_message.author} "
            f"({original_message.author.id})",
            inline=False,
        )
        embed.add_field(
            name="Context", value=f"[Jump to Message]({npc_message_url})", inline=False
        )
        embed.add_field(name="Message", value=npc_message_content)
        await log_channel.send(embed=embed)
    async def role_info(self, ctx, role: discord.Role):
        if role is None:
            return await ctx.send(
                f"{config.NO} `role` is neither a role on this server, nor on the Democraciv server."
            )

        if role.guild.id != ctx.guild.id:
            description = f":warning:  This role is from the {self.bot.dciv.name} server, not from this server!"
            role_name = role.name
        else:
            description = ""
            role_name = f"{role.name} {role.mention}"

        if role != role.guild.default_role:
            role_members = ("\n".join(
                [f"{member.mention} {member}" for member in role.members])
                            or "-")
        else:
            role_members = "*Too long to display.*"

        embed = text.SafeEmbed(title="Role Information",
                               description=description,
                               colour=role.colour)

        embed.add_field(name="Role", value=role_name, inline=False)
        embed.add_field(name="ID", value=role.id, inline=False)
        embed.add_field(name="Created on",
                        value=role.created_at.strftime("%B %d, %Y"),
                        inline=True)
        embed.add_field(name="Colour", value=role.colour, inline=True)
        embed.add_field(name=f"Members ({len(role.members)})",
                        value=role_members,
                        inline=False)
        await ctx.send(embed=embed)
Example #12
0
    async def admin(self, ctx):
        """What is a Nation Admin?"""

        p = config.BOT_PREFIX

        embed = text.SafeEmbed(
            description=f"Nation Admins are allowed to make roles and "
            f"channels on the {self.bot.dciv.name} server that are "
            f"specific for their nation (`{p}help Nation`).\n\nAdditionally, they are "
            f"allowed to create, edit and delete political parties "
            f"(`{p}help Political Parties`).\n\nNation Admins can also pin messages "
            f"in every category that belongs to their nation.")

        embed.set_author(name=self.bot.mk.NATION_NAME,
                         icon_url=self.bot.mk.safe_flag)

        try:
            role = self.bot.get_democraciv_role(mk.DemocracivRole.NATION_ADMIN)

            if role:
                fmt = [m.mention for m in role.members] or ["-"]
                embed.add_field(name="Nation Admins", value="\n".join(fmt))

        except exceptions.RoleNotFoundError:
            pass

        await ctx.send(embed=embed)
    async def mod_request_listener(self, message):
        # If it's a command, ignore
        if (await self.bot.get_context(message)).valid:
            return

        if message.guild != self.bot.dciv:
            return

        if message.author.bot:
            return

        try:
            mod_role = self.bot.get_democraciv_role(
                mk.DemocracivRole.MODERATION)
        except exceptions.RoleNotFoundError:
            return

        if mod_role is None:
            return

        try:
            mod_channel = self.bot.get_democraciv_channel(
                mk.DemocracivChannel.MODERATION_NOTIFICATIONS_CHANNEL)
        except exceptions.ChannelNotFoundError:
            return

        if mod_role in message.role_mentions:
            embed = text.SafeEmbed(
                title=f":pushpin:  New Request in #{message.channel.name}",
                description=message.content,
            )
            embed.add_field(name="From", value=message.author.mention)
            embed.add_field(name="Original",
                            value=f"[Jump to Message]({message.jump_url})")
            await mod_channel.send(embed=embed)
 async def modguidelines(self, ctx):
     """Link to DerJonas' Democraciv Moderation Guidelines"""
     link = token.MOD_GUIDELINES or "https://hastebin.com/afijavahox.coffeescript"
     embed = text.SafeEmbed(
         title="DerJonas' Democraciv Moderation Guidelines & Procedures",
         url=link)
     await self.safe_send_mod_links(ctx, embed)
Example #15
0
    async def statistics(self, ctx):
        """See how much of every currency is currently in circulation and other statistics"""

        response = await self.request(BankRoute("GET", f"currencies/"))
        currencies = await response.json()

        stat_response = await self.request(BankRoute("GET", f"statistics/"))
        stats = await stat_response.json()

        embed = text.SafeEmbed(
            description=
            f"\n\nThere are a total of {stats['total_bank_accounts']} open bank accounts across "
            f"all currencies with a total of {stats['total_transactions']} transactions between "
            f"all of them.\n\nThe shown circulation of a currency does not include any currency reserves that "
            f"were provided by the {self.BANK_NAME} when this currency "
            f"was originally created.\n\nThe velocity is calculated as the amount of currency transferred "
            f"in the last 7 days divided by its total circulation.")

        embed.set_author(name=self.BANK_NAME, icon_url=self.BANK_ICON_URL)

        for currency in currencies["result"]:
            as_object = self.get_currency(currency["code"])

            stat = stats["currencies"]["detail"][currency["code"].upper()]
            value = (
                f"Circulation: {as_object.with_amount(currency['circulation'])}\n"
                f"Transactions: {stat['transactions']}\n"
                f"Bank Accounts: {stat['bank_accounts']}\n"
                f"Velocity in the last 7 days: {stat['velocity']:.3f}")

            embed.add_field(name=currency["name"], value=value)

        await ctx.send(embed=embed)
    async def dog(self, ctx):
        """Just for Tay: A random image or video of a dog"""

        async with self.bot.session.get("https://random.dog/woof") as resp:
            if resp.status != 200:
                return await ctx.send(f"{config.NO} No dog found :(")

            filename = await resp.text()
            url = f"https://random.dog/{filename}"
            filesize = ctx.guild.filesize_limit if ctx.guild else 8388608

            if filename.endswith((".mp4", ".webm")):
                async with ctx.typing():
                    async with self.bot.session.get(url) as other:
                        if other.status != 200:
                            return await ctx.send(
                                f"{config.NO} Could not download dog video :(")

                        if int(other.headers["Content-Length"]) >= filesize:
                            return await ctx.send(
                                f"{config.NO} Video was too big to upload, watch it here instead: {url}"
                            )

                        fp = io.BytesIO(await other.read())
                        await ctx.send(file=discord.File(fp, filename=filename)
                                       )
            else:
                embed = text.SafeEmbed(title="Random Dog")
                embed.set_image(url=url)
                embed.set_footer(text="Just for Taylor.")
                await ctx.send(embed=embed)
    async def tag_listener(self, message):
        if message.author.bot:
            return

        ctx: context.CustomContext = await self.bot.get_context(message)

        if ctx.valid or not ctx.prefix:
            return

        tag_name = message.content[len(ctx.prefix) :]
        tag_details = await self.resolve_tag_name(tag_name, message.guild)

        if tag_details is None:
            return

        tag_content_type = self.get_tag_content_type(tag_details["content"])

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

                try:
                    await message.channel.send(embed=embed)
                except discord.HTTPException:
                    await message.channel.send(
                        discord.utils.escape_mentions(tag_details["content"])
                    )

            elif tag_content_type is TagContentType.VIDEO:
                # discord doesn't allow videos in embeds
                await message.channel.send(
                    discord.utils.escape_mentions(tag_details["content"])
                )

            else:
                embed = text.SafeEmbed(
                    title=tag_details["title"], description=tag_details["content"]
                )

                await message.channel.send(embed=embed)

        else:
            await message.channel.send(
                discord.utils.escape_mentions(tag_details["content"])
            )
    async def taginfo(self, ctx: context.CustomContext, *, tag: Fuzzy[Tag]):
        """Info about a tag"""

        pretty_aliases = (
            ", ".join([f"`{config.BOT_PREFIX}{alias}`" for alias in tag.aliases])
        ) or "-"

        embed = text.SafeEmbed(title=tag.title)

        is_global = "Yes" if tag.is_global else "No"
        is_embedded = "Embed" if tag.is_embedded else "Plain Text"

        if isinstance(tag.author, discord.Member):
            embed.add_field(name="Author", value=tag.author.mention, inline=False)
            embed.set_author(
                name=tag.author.name,
                icon_url=tag.author.display_avatar.url,
            )

        elif isinstance(tag.author, discord.User):
            embed.add_field(
                name="Author",
                value=f"*The author of this tag left this server.*\n"
                f"*You can claim this tag to make it yours with*\n"
                f"`{config.BOT_PREFIX}tag claim {tag.name}`",
                inline=False,
            )
            embed.set_author(
                name=tag.author.name,
                icon_url=tag.author.display_avatar.url,
            )

        elif tag.author is None:
            embed.add_field(
                name="Author",
                value=f"*The author of this tag left this server.*\n"
                f"*You can claim this tag to make it yours with*\n"
                f"`{config.BOT_PREFIX}tag claim {tag.name}`",
                inline=False,
            )

        embed.add_field(name="Global Tag", value=is_global, inline=True)
        embed.add_field(name="Tag Format", value=is_embedded, inline=True)
        embed.add_field(name="Uses", value=tag.uses, inline=False)
        embed.add_field(
            name="Collaborators",
            value="\n".join(
                [f"{c.mention} {c}" for c in tag.collaborators]
                or [
                    f"*The owner of this tag can add other people as "
                    f"collaborators for this tag, so that they can "
                    f"edit and add & "
                    f"remove aliases, with "
                    f"`{config.BOT_PREFIX}tag share {tag.name}`.*\n\n-"
                ]
            ),
        )
        embed.add_field(name="Aliases", value=pretty_aliases, inline=False)
        await ctx.send(embed=embed)
Example #19
0
    async def on_resumed(self):
        logging.info("Resumed connection to Discord")
        channel = self.get_channel(config.BOT_TECHNICAL_NOTIFICATIONS_CHANNEL)

        if channel:
            embed = text.SafeEmbed(
                title=f"{config.YES}  Resumed connection to Discord")
            await channel.send(embed=embed)
    async def allcmds(self, ctx):
        """List all commands"""

        description_text = []
        field_text = []

        amounts = 0
        i = 0
        p = config.BOT_PREFIX

        for name, cog in sorted(self.bot.cogs.items()):
            if cog.hidden:
                continue

            cog_cmds = sorted(
                [
                    command
                    for command in cog.walk_commands() if not command.hidden
                ],
                key=lambda c: c.qualified_name,
            )

            amounts += len(cog_cmds)

            commands_list = []

            for command in cog_cmds:
                if not command.hidden:
                    commands_list.append(f"`{p}{command.qualified_name}`")

            if i == 0:
                description_text.append(f"**__{cog.qualified_name}__**\n")
                description_text.append("\n".join(commands_list))
                description_text.append("\n")
            elif i < 8:
                description_text.append(f"\n**__{cog.qualified_name}__**\n")
                description_text.append("\n".join(commands_list))
                description_text.append("\n")
            else:
                field_text.append(f"\n**__{cog.qualified_name}__**\n")
                field_text.append("\n".join(commands_list))
                field_text.append("\n")

            i += 1

        embed = text.SafeEmbed(
            title=f"All Commands ({amounts})",
            description=
            f"For more detailed explanations and example usage of commands, "
            f"use `{p}help`, `{p}help <Category>`, "
            f"or `{p}help <command>`."
            f"\n\n{' '.join(description_text)}",
        )

        if field_text:
            embed.add_field(name="\u200b", value=" ".join(field_text))

        await ctx.send(embed=embed)
Example #21
0
    async def tagcreation(self, ctx: context.CustomContext):
        """Allow everyone to make tags on this server, or just Administrators"""

        settings = await self.ensure_guild_settings(ctx.guild.id)
        is_allowed = settings["tag_creation_allowed"]

        pretty_is_allowed = "Only Administrators" if not is_allowed else "Everyone"

        embed = text.SafeEmbed(
            description=f"React with the {config.GUILD_SETTINGS_GEAR} emoji"
            f" to change this setting.", )

        embed.set_author(name=f"Tag Creation on {ctx.guild.name}",
                         icon_url=ctx.guild_icon)
        embed.add_field(name="Allowed Tag Creators", value=pretty_is_allowed)

        info_embed = await ctx.send(embed=embed)

        if await ctx.ask_to_continue(
                message=info_embed,
                emoji=config.GUILD_SETTINGS_GEAR,
                label="Change Settings",
        ):
            view = SelectTagCreationView(ctx)

            await ctx.send(
                f"{config.USER_INTERACTION_REQUIRED} Who should be able to create new tags "
                f"on this server with `{config.BOT_PREFIX}tag add`?",
                view=view,
            )

            result = await view.prompt()

            if not result:
                return

            if result == "Everyone":
                await self.bot.db.execute(
                    "UPDATE guild SET tag_creation_allowed = true WHERE id = $1",
                    ctx.guild.id,
                )
                await ctx.send(
                    f"{config.YES} Everyone can now make tags with `{config.BOT_PREFIX}tag add` on this server."
                )

            elif result == "Administrators":
                await self.bot.db.execute(
                    "UPDATE guild SET tag_creation_allowed = false WHERE id = $1",
                    ctx.guild.id,
                )
                await ctx.send(
                    f"{config.YES} Only Administrators can now make tags with "
                    f"`{config.BOT_PREFIX}tag add` on this server.")

            await self.bot.update_guild_config_cache()
Example #22
0
    async def send_command_help(self, command):
        embed = text.SafeEmbed()
        await self.common_command_formatting(embed, command)

        parent_name = f"{command.full_parent_name} " if command.full_parent_name else ""
        aliases = [f"`{config.BOT_PREFIX}{parent_name}{a}`" for a in command.aliases]

        if aliases:
            embed.add_field(name="Aliases", value=", ".join(aliases))

        await self.context.send(embed=embed)
    async def time(self, ctx, *, zone: str):
        """Displays the current time of a specified time zone"""

        # catch bird time - otherwise 'msk' would be matched to Asia/Omsk, not Moscow
        if zone.lower() == "msk":
            zone = "Europe/Moscow"

        try:
            tz = pytz.timezone(zone)
        except pytz.UnknownTimeZoneError:
            match = process.extract(zone, pytz.all_timezones, limit=5)

            menu = text.FuzzyChoose(
                ctx,
                question="Which time zone did you mean?",
                choices=[zone for zone, _ in match],
            )
            zone = await menu.prompt()

            if not zone:
                return

            tz = pytz.timezone(zone)

        title = str(tz)

        # blame POSIX for this
        # https://stackoverflow.com/a/4009126

        if "+" in zone:
            title = zone
            fixed = zone.replace("+", "-")
            tz = pytz.timezone(fixed)
        elif "-" in zone:
            title = zone
            fixed = zone.replace("-", "+")
            tz = pytz.timezone(fixed)

        utc_now = discord.utils.utcnow()
        date = utc_now.astimezone(tz)

        embed = text.SafeEmbed(title=f":clock1:  Current Time in {title}")
        embed.add_field(name="Date",
                        value=date.strftime("%A, %B %d %Y"),
                        inline=False)
        embed.add_field(
            name="Time (12-Hour Clock)",
            value=date.strftime("%I:%M:%S %p"),
            inline=False,
        )
        embed.add_field(name="Time (24-Hour Clock)",
                        value=date.strftime("%H:%M:%S"),
                        inline=False)
        await ctx.send(embed=embed)
Example #24
0
    async def send_initial_message(self, ctx, channel):
        if not self.send_intro:
            return await super().send_initial_message(ctx, channel)

        embed = text.SafeEmbed(title="Welcome to the Democraciv Bot")
        embed.description = BOT_INTRO
        embed.set_author(
            name=f"Made by {ctx.bot.owner}",
            icon_url=ctx.bot.owner.display_avatar.url,
        )
        self.current_page = -1
        return await channel.send(embed=embed)
    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 #26
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 #27
0
    async def close(self):
        """Closes the aiohttp ClientSession, the connection pool to the PostgreSQL database and the bot itself."""
        logging.info("Closing bot...")
        channel = self.get_channel(config.BOT_TECHNICAL_NOTIFICATIONS_CHANNEL)

        if channel:
            embed = text.SafeEmbed(
                title=f"{config.YES}  Bot is shutting down...")
            await channel.send(embed=embed)

        await super().close()
        await self.session.close()
        await self.db.close()
    async def cat(self, ctx):
        """A random cat"""

        async with self.bot.session.get(
                "https://api.thecatapi.com/v1/images/search") as response:
            if response.status != 200:
                return await ctx.send(f"{config.NO} No cat found :(")

            js = await response.json()

            embed = text.SafeEmbed(title="Random Cat")
            embed.set_image(url=js[0]["url"])
            await ctx.send(embed=embed)
Example #29
0
 async def send_initial_message(self, ctx, channel):
     read = "Deny" if self.overwrites.read_messages else "Allow"
     send = "Deny" if self.overwrites.send_messages else "Allow"
     embed = text.SafeEmbed(
         title=
         f"{config.USER_INTERACTION_REQUIRED}  Which Permissions in #{self.channel.name} do you want "
         f"to change?",
         description=
         f"Select as many things as you want, then click the {config.YES} button to continue, "
         f"or {config.NO} to cancel.\n\n"
         f":one: {read} Read Messages Permission for `{self.role}` in {self.channel.mention}\n"
         f":two: {send} Send Messages Permission for `{self.role}` in {self.channel.mention}",
     )
     return await ctx.send(embed=embed)
    async def _person_stats(self, ctx, person):
        amount = await self.bot.db.fetch(
            "SELECT COUNT(name) FROM tag WHERE author = $1 "
            "UNION ALL "
            "SELECT COUNT(name) FROM tag WHERE author = $1 AND guild_id = $2 "
            "UNION ALL "
            "SELECT COUNT(name) FROM tag WHERE author = $1 AND global = true ",
            person.id,
            ctx.guild.id,
        )

        top_tags = await self.bot.db.fetch(
            "SELECT name, uses FROM tag WHERE author = $1 AND guild_id = $2 "
            "ORDER BY uses DESC LIMIT 5",
            person.id,
            ctx.guild.id,
        )

        top_global_tags = await self.bot.db.fetch(
            "SELECT name, uses FROM tag WHERE author = $1 AND global = true "
            "ORDER BY uses DESC LIMIT 5",
            person.id,
        )

        embed = text.SafeEmbed()
        embed.set_author(name=person.display_name, icon_url=person.display_avatar.url)

        embed.add_field(name="Amount of Tags from any Server", value=amount[0]["count"])
        embed.add_field(
            name="Amount of Global Tags from any Server", value=amount[2]["count"]
        )
        embed.add_field(
            name="Amount of Tags from this Server",
            value=amount[1]["count"],
            inline=False,
        )

        embed.add_field(
            name="Top Tags from this Server (Global and Local)",
            value=self._fmt_stats(top_tags),
            inline=False,
        )
        embed.add_field(
            name="Top Global Tags from any Server",
            value=self._fmt_stats(top_global_tags),
            inline=False,
        )

        await ctx.send(embed=embed)