Beispiel #1
0
async def role_list(ctx):
    """List level/role associations."""
    guild = ctx.message.guild
    user = ctx.message.author

    embed = Embed(description="", colour=user.colour)
    embed.set_author(
        name=f"Current Role - Level Links for {guild.name}",
        icon_url=guild.icon_url
    )

    role_text = []
    for role in guild.roles:
        role_info = db.role(role)
        role_level = await role_info.level()
        remove_role_id = await role_info.remove_role()
        if role_level is not None:
            if remove_role_id is not None:
                role_name = next(r.name for r in guild.roles if r.id == remove_role_id) or "Deleted Role"
                role_text.append(f"**• {role} →** {role_level} (Removes: {role_name})\n")
            else:
                role_text.append(f"**• {role} →** {role_level}\n")

    if not role_text:
        embed.description = "None"
    else:
        embed.description = "**Role** → Level\n" + "".join(role_text)

    await ctx.send(embed=embed)
Beispiel #2
0
async def add_category(embed: Embed, category: str, backgrounds: Dict[str, str]) -> NoReturn:
    max_backgrounds = 18

    bg_urls = [
        f"[{background_name}]({backgrounds[background_name]})"
        for background_name in sorted(backgrounds)
    ]
    bgs = ", ".join(bg_urls[:min(max_backgrounds, len(bg_urls))])
    if len(bg_urls) >= max_backgrounds:
        bgs += "..."
    embed.add_field(name=category.upper(), value=bgs, inline=False)
Beispiel #3
0
async def all_catagories(ctx) -> NoReturn:
    user = ctx.message.author
    backgrounds = await db.backgrounds()

    embed = Embed(description="", colour=user.colour)
    embed.set_author(
        name=f"All Backgrounds for {ctx.bot.user.name}",
        icon_url=ctx.bot.user.avatar_url,
    )
    for category, bgs in backgrounds.items():
        await add_category(embed, category, bgs)
    await ctx.send(embed=embed)
Beispiel #4
0
async def single_catagory(ctx, catagory: str) -> NoReturn:
    user = ctx.message.author
    backgrounds = await db.backgrounds()
    if catagory not in backgrounds:
        return await ctx.send(f"**Invalid Background Type. ({', '.join(backgrounds)})**")

    embed = Embed(description="", colour=user.colour)
    embed.set_author(
        name=f"{catagory.title()} Backgrounds for {ctx.bot.user.name}",
        icon_url=ctx.bot.user.avatar_url,
    )
    await add_category(embed, catagory, backgrounds[catagory])
    await ctx.send(embed=embed)
Beispiel #5
0
    async def output_result(self, channel: Channel):
        embed = Embed()
        embed.colour = CodeHandlerState.colour(self.state)
        embed.description = "**Status:** {}".format(CodeHandlerState.get_name(self.state))
        embed.add_field(name="Compiler Output", value="```\n{}\n```".format(self.compile_output), inline=False)
        if self.state != CodeHandlerState.failed_compile:
            embed.add_field(name="Code Output", value="```\n{}\n```".format(self.output), inline=False)

        await client.send_message(channel, embed=embed)
Beispiel #6
0
    def __init__(
            self, error_subject, error_details, *args, e=None,
            error_type=ErrorTypes.RECOVERABLE, edit_message=None, autodelete=0,
            use_embed=True, embed_fields=[], embed_format={}, serious=False, editable=True):
        """
        Arguments:
        error_subject -- The error title. Generally the plugin name.
        error_details -- Primary error message.
        args -- A list of additional errors that get appended after the details.

        Keyword arguments:
        e -- The provided exception object itself.
        error_type -- Determines if the bot can recover from the exception.
        edit_message -- Edits the given message with the exception text or embed.
        autodelete -- Deletes after the given number of seconds, unless it is 0.
        use_embed -- The error should be displayed as an embed.
        embed_fields -- Additional fields used for providing titled descriptions of the error.
        embed_format -- Used to format the strings of the values in the embed fields.
        serious -- If True, always uses the :warning: emoji.
        editable -- Whether or not the error displays an "issuing command is editable" note.
        """
        self.error_type = error_type
        self.error_subject = str(error_subject)
        self.error_details = str(error_details)
        self.error_other = args
        self.provided_exception = e
        self.autodelete = 0 if autodelete is None else autodelete
        self.use_embed = use_embed
        self.editable = editable
        self.traceback = ''
        self.other_details = '\n'.join([str(arg) for arg in self.error_other])
        self.error_message = "`{subject} error: {details}`\n{others}".format(
            subject=self.error_subject,
            details=self.error_details,
            others=self.other_details)
        emoji = ':warning:' if serious or random() > 0.01 else ':thinking:'
        self.embed = Embed(
            title='{} {} error'.format(emoji, self.error_subject),
            description='{}\n{}'.format(self.error_details, self.other_details),
            colour=Colour(0xffcc4d))

        if self.provided_exception:
            if isinstance(self.provided_exception, BotException):
                given_error = '{}'.format(self.provided_exception.error_details)
                embed_fields = self.provided_exception.embed_fields
            else:
                given_error = '`{}: {}`'.format(
                    type(self.provided_exception).__name__, self.provided_exception)
            self.error_message += '\nGiven error:\n{}'.format(given_error)
            embed_fields = [('Given error:', given_error)] + embed_fields
            if self.provided_exception.__traceback__:
                self.traceback = traceback.format_tb(self.provided_exception.__traceback__)
            else:
                self.traceback = traceback.format_exc()

        self.embed_fields = embed_fields
        for name, value in embed_fields:
            self.embed.add_field(name=name, value=value.format(**embed_format), inline=False)

        logger.error(self.error_message)

        # If non-recoverable, quit
        if error_type in (ErrorTypes.STARTUP, ErrorTypes.FATAL, ErrorTypes.INTERNAL):
            traceback.print_exc()
            sys.exit()

        # Edit the given message with the error
        if edit_message:
            content, embed = ('', self.embed) if self.use_embed else (str(self), None)
            asyncio.ensure_future(edit_message.edit(content=content, embed=embed))
def set_footer(embed: Embed, ctx: commands.Context) -> Embed:
    """Set the discord.Embed's footer"""
    return embed.set_footer(icon_url=ctx.author.avatar_url, text=f"{ctx.author.display_name} • {get_time()}")
Beispiel #8
0
 async def rank(self,ctx,member : Member = None):
     if member != None:
         what = ["xp","lvl","repo"]
         result = select("Members",what,f"id = '{member.id}'")
         for row in result:
             xp = int(row[0])
             lvl = int(row[1])
             repo = int(row[2])
         em = Embed(
             colour = Colour.purple()
         )
         em.set_author(name = member.name,icon_url = member.avatar_url)
         em.add_field(name='Уровень:',value=lvl)
         em.add_field(name='Xp:',value=xp)
         em.add_field(name="Осталось до следушего уровня:",value=str(lvl*(lvl * 50)- xp)+ ' xp')
         em.add_field(name='Количество "Спасибо":',value=repo)
         await ctx.send(embed = em)
     else:
         what = ["xp","lvl","repo"]
         result = select("Members",what,f"id = '{ctx.message.author.id}'")
         for row in result:
             xp = int(row[0])
             lvl = int(row[1])
             repo = int(row[2])
         em = Embed(
             colour = Colour.purple()
         )
         em.set_author(name = ctx.message.author.name,icon_url = ctx.message.author.avatar_url)
         em.add_field(name='Ваш уровень:',value=lvl)
         em.add_field(name='Ваш Xp:',value=xp)
         em.add_field(name="Вам осталось до следушего уровня:",value=str(lvl*(lvl * 50)- xp)+ ' xp')
         em.add_field(name='Ваше количество "Спасибо":',value=repo)
         await ctx.send(embed = em)
Beispiel #9
0
class Fun(Cog):
    """
    Commands for fun!
    """

    # Embed sent when users try to ping staff
    ping_embed = (Embed(
        colour=0xFF0000,
        description=
        "⚠ **Please make sure you have taken the following into account:** "
    ).set_footer(
        text=
        "To continue with the ping, react \N{THUMBS UP SIGN}, To delete this message and move on,"
        " react \N{THUMBS DOWN SIGN}"
    ).add_field(
        name="Cyber Discovery staff will not provide help for challenges.",
        value=
        "If you're looking for help, feel free to ask questions in one of our topical channels.",
    ).add_field(
        name="Make sure you have emailed support before pinging here.",
        value=
        "`[email protected]` are available to answer any and all questions!",
    ))

    def __init__(self, bot: Bot):
        self.bot = bot
        self.staff_role = None
        self.quote_channel = None
        self.fake_staff_role = None

    async def migrate_quotes(self):
        """Create and initialise the `quotes` table with user quotes."""
        conn = await asyncpg.connect(
            host=PostgreSQL.PGHOST,
            port=PostgreSQL.PGPORT,
            user=PostgreSQL.PGUSER,
            password=PostgreSQL.PGPASSWORD,
            database=PostgreSQL.PGDATABASE,
        )
        await conn.execute(
            "CREATE TABLE IF NOT EXISTS quotes (quote_id bigint PRIMARY KEY, author_id bigint)"
        )
        quote_channel = self.bot.get_channel(QUOTES_CHANNEL_ID)
        async for quote in quote_channel.history(limit=None):
            await self.add_quote_to_db(conn, quote)
        await conn.close()

    @Cog.listener()
    async def on_ready(self):
        guild = self.bot.guilds[0]

        if self.staff_role is None:
            self.staff_role = guild.get_role(STAFF_ROLE_ID)

        if self.fake_staff_role is None:
            self.fake_staff_role = guild.get_role(FAKE_ROLE_ID)

        await self.migrate_quotes()

    @Cog.listener()
    async def on_message(self, message: Message):
        # If a new quote is added, add it to the database.
        if message.channel.id == QUOTES_CHANNEL_ID and (
                message.author.id == QUOTES_BOT_ID
                or message.mentions is not None):
            conn = await asyncpg.connect(
                host=PostgreSQL.PGHOST,
                port=PostgreSQL.PGPORT,
                user=PostgreSQL.PGUSER,
                password=PostgreSQL.PGPASSWORD,
                database=PostgreSQL.PGDATABASE,
            )

            await self.add_quote_to_db(conn, message)
            await conn.close()
            print(f"Message #{message.id} added to database.")

        if self.fake_staff_role in message.role_mentions and not message.author.bot:
            # A user has requested to ping official staff
            sent = await message.channel.send(embed=self.ping_embed,
                                              delete_after=30)
            await sent.add_reaction("\N{THUMBS UP SIGN}")
            await sent.add_reaction("\N{THUMBS DOWN SIGN}")

            def check(reaction, user):
                """Check if the reaction was valid."""
                return all((user == message.author, str(reaction.emoji)
                            in "\N{THUMBS UP SIGN}\N{THUMBS DOWN SIGN}"))

            try:
                # Get the user's reaction
                reaction, _ = await self.bot.wait_for("reaction_add",
                                                      timeout=30,
                                                      check=check)
            except asyncio.TimeoutError:
                pass
            else:
                if str(reaction) == "\N{THUMBS UP SIGN}":
                    # The user wants to continue with the ping
                    await self.staff_role.edit(mentionable=True)
                    staff_ping = Embed(
                        title="This user has requested an official staff ping!",
                        colour=0xFF0000,
                        description=message.content,
                    ).set_author(
                        name=
                        f"{message.author.name}#{message.author.discriminator}",
                        icon_url=message.author.avatar_url,
                    )
                    # Send the embed with the user's content
                    await message.channel.send(self.staff_role.mention,
                                               embed=staff_ping)
                    await self.staff_role.edit(mentionable=False)
                    # Delete the original message
                    await message.delete()
            finally:
                await sent.delete()

        ctx = await self.bot.get_context(message)

        if ctx.valid:
            # Don't react to valid commands
            return

        for word in message.content.lower().split():
            # Check if the message contains a trigger
            if word in REACT_TRIGGERS:
                to_react = REACT_TRIGGERS[word]

                if len(to_react) > 1:  # We have a string to react with
                    await emojify(message, to_react)
                else:
                    await message.add_reaction(to_react)

                return  # Only one auto-reaction per message

        # Adds waving emoji when a new user joins.
        if "Welcome to the Cyber Discovery" in message.content and message.author.id == WELCOME_BOT_ID:
            await message.add_reaction("\N{WAVING HAND SIGN}")

    @command()
    async def lmgtfy(self, ctx: Context, *args: str):
        """
        Returns a LMGTFY URL for a given user argument.
        """

        # Flag checking.
        delete = False
        ie_flag = False
        if "-d" in args:
            delete = True
        if "-ie" in args:
            ie_flag = True

        # Creates a lmgtfy.com url for the given query.
        request_data = {
            "q": " ".join(arg for arg in args if not arg.startswith("-")),
            "ie": int(ie_flag)
        }
        url = "https://lmgtfy.com/?" + urlencode(request_data)

        await ctx.send(url)

        if delete:
            await ctx.message.delete()

    # Ratelimit to two usages per user every minute and 4 usages per minute per channel
    @command(aliases=["emojify"])
    @cooldown(1, 60, BucketType.user)
    @cooldown(4, 60, BucketType.channel)
    async def react(self, ctx, *, message: str):
        """
        Emojifies a given string, and reacts to a previous message
        with those emojis.
        """
        limit, _, output = message.partition(" ")
        if limit.isdigit():
            limit = int(limit)
        else:
            output = message
            limit = 2
        async for target in ctx.channel.history(limit=limit):
            pass
        await emojify(target, output)

    @command()
    async def xkcd(self, ctx: Context, number: str = None):
        """
        Fetches xkcd comics.
        If number is left blank, automatically fetches the latest comic.
        If number is set to '?', a random comic is fetched.
        """

        # Creates endpoint URI
        if number is None or number == "?":
            endpoint = "https://xkcd.com/info.0.json"
        else:
            endpoint = f"https://xkcd.com/{number}/info.0.json"

        # Fetches JSON data from endpoint
        async with ClientSession() as session:
            async with session.get(endpoint) as response:
                data = await response.json()

        # Updates comic number
        if number == "?":
            number = randint(1, int(data["num"]))  # noqa: B311
            endpoint = f"https://xkcd.com/{number}/info.0.json"
            async with ClientSession() as session:
                async with session.get(endpoint) as response:
                    data = await response.json()
        else:
            number = data["num"]

        # Creates date object (Sorry, but I'm too tired to use datetime.)
        date = f"{data['day']}/{data['month']}/{data['year']}"

        # Creates Rich Embed, populates it with JSON data and sends it.
        comic = Embed()
        comic.title = data["safe_title"]
        comic.set_footer(text=data["alt"])
        comic.set_image(url=data["img"])
        comic.url = f"https://xkcd.com/{number}"
        comic.set_author(name="xkcd",
                         url="https://xkcd.com/",
                         icon_url="https://xkcd.com/s/0b7742.png")
        comic.add_field(name="Number:", value=number)
        comic.add_field(name="Date:", value=date)
        comic.add_field(name="Explanation:",
                        value=f"https://explainxkcd.com/{number}")

        await ctx.send(embed=comic)

    @command()
    async def quotes(self, ctx: Context, member: FormerUser = None):
        """
        Returns a random quotation from the #quotes channel.
        A user can be specified to return a random quotation from that user.
        """
        conn = await asyncpg.connect(
            host=PostgreSQL.PGHOST,
            port=PostgreSQL.PGPORT,
            user=PostgreSQL.PGUSER,
            password=PostgreSQL.PGPASSWORD,
            database=PostgreSQL.PGDATABASE,
        )
        quote_channel = self.bot.get_channel(QUOTES_CHANNEL_ID)

        if member is None:
            message_id = await conn.fetchval(
                "SELECT quote_id FROM quotes ORDER BY random() LIMIT 1;"
            )  # fetchval returns first value by default
        else:
            message_id = await conn.fetchval(
                "SELECT quote_id FROM quotes WHERE author_id=$1 ORDER BY random() LIMIT 1;",
                member.id)
            if message_id is None:
                await ctx.send("No quotes for that user.")
                return

        await conn.close()
        message = await quote_channel.fetch_message(message_id)
        embed = None
        content = message.clean_content
        attachment_urls = [
            attachment.url for attachment in message.attachments
        ]

        if message.embeds:
            embed = message.embeds[0]
        elif len(attachment_urls) == 1:
            image_url = attachment_urls.pop(0)
            embed = Embed()
            embed.set_image(url=image_url)

        for url in attachment_urls:
            content += "\n" + url

        await ctx.send(content, embed=embed)

    async def add_quote_to_db(self, conn: asyncpg.connection.Connection,
                              quote: Message):
        """
        Adds a quote message ID to the database, and attempts to identify the author of the quote.
        """
        author_id = None
        if quote.author.id == QUOTES_BOT_ID:
            if not quote.embeds:
                return
            embed = quote.embeds[0]
            icon_url = embed.author.icon_url
            if type(icon_url) == embeds._EmptyEmbed or 'twimg' in icon_url:
                author_id = QUOTES_BOT_ID
            elif 'avatars' in icon_url:
                author_id = int(icon_url.split('/')[4])
            else:
                author_info = embed.author.name.split("#")
                author = get(quote.guild.members,
                             name=author_info[0],
                             discriminator=author_info[1])
                author_id = author.id if author is not None else None
        else:
            author_id = quote.mentions[0].id if quote.mentions else None
        if author_id is not None:
            await conn.execute(
                "INSERT INTO quotes(quote_id, author_id) VALUES($1, $2) ON CONFLICT DO NOTHING;",
                quote.id,
                author_id,
            )
        else:
            await conn.execute(
                "INSERT INTO quotes(quote_id) VALUES($1) ON CONFLICT DO NOTHING;",
                quote.id)
        print(f"Quote ID: {quote.id} has been added to the database.")

    async def create_text_image(self, ctx: Context, person: str, text: str):
        """
        Creates an image of a given person with the specified text.
        """
        if len(text) > 100:
            return await ctx.send(
                ":no_entry_sign: Your text must be shorter than 100 characters."
            )
        drawing_text = textwrap.fill(text, 20)
        font = ImageFont.truetype("cdbot/resources/Dosis-SemiBold.ttf", 150)

        text_layer = Image.new("RGBA", (1920, 1080), (0, 0, 0, 0))
        text_layer_drawing = ImageDraw.Draw(text_layer)
        text_layer_drawing.text((0, 0),
                                drawing_text,
                                fill=(0, 0, 0),
                                align="center",
                                font=font)

        cropped_text_layer = text_layer.crop(text_layer.getbbox())
        cropped_text_layer.thumbnail((170, 110))

        image = Image.open(f"cdbot/resources/{person}SaysBlank.png")

        x = int((image.width / 5 + 20) - (cropped_text_layer.width / 2))
        y = int((image.height / 5 + 50 / 2) - (cropped_text_layer.height / 2))

        image.paste(cropped_text_layer, (x, y), cropped_text_layer)
        image_bytes = BytesIO()
        image.save(image_bytes, format="PNG")
        image_bytes.seek(0)
        await ctx.send(file=File(image_bytes, filename=f"{person}.png"))

    @command()
    async def agentj(self, ctx: Context, *, text: str):
        """
        Creates an image of Agent J with the specified text.
        """
        await self.create_text_image(ctx, "AgentJ", text)

    @command()
    async def jibhat(self, ctx: Context, *, text: str):
        """
        Creates an image of Jibhat with the specified text.
        """
        await self.create_text_image(ctx, "Jibhat", text)

    @command()
    async def agentq(self, ctx: Context, *, text: str):
        """
        Creates an image of Agent Q with the specified text.
        """
        await self.create_text_image(ctx, "AgentQ", text)

    @command()
    async def angryj(self, ctx: Context, *, text: str):
        """
        Creates an image of Angry Agent J with the specified text.
        """
        await self.create_text_image(ctx, "AngryJ", text)

    @command()
    async def angrylyne(self, ctx: Context, *, text: str):
        """
        Creates an image of Angry James Lyne with the specified text.
        """
        await self.create_text_image(ctx, "AngryLyne", text)
Beispiel #10
0
    async def on_message(self, message: Message):
        # If a new quote is added, add it to the database.
        if message.channel.id == QUOTES_CHANNEL_ID and (
                message.author.id == QUOTES_BOT_ID
                or message.mentions is not None):
            conn = await asyncpg.connect(
                host=PostgreSQL.PGHOST,
                port=PostgreSQL.PGPORT,
                user=PostgreSQL.PGUSER,
                password=PostgreSQL.PGPASSWORD,
                database=PostgreSQL.PGDATABASE,
            )

            await self.add_quote_to_db(conn, message)
            await conn.close()
            print(f"Message #{message.id} added to database.")

        if self.fake_staff_role in message.role_mentions and not message.author.bot:
            # A user has requested to ping official staff
            sent = await message.channel.send(embed=self.ping_embed,
                                              delete_after=30)
            await sent.add_reaction("\N{THUMBS UP SIGN}")
            await sent.add_reaction("\N{THUMBS DOWN SIGN}")

            def check(reaction, user):
                """Check if the reaction was valid."""
                return all((user == message.author, str(reaction.emoji)
                            in "\N{THUMBS UP SIGN}\N{THUMBS DOWN SIGN}"))

            try:
                # Get the user's reaction
                reaction, _ = await self.bot.wait_for("reaction_add",
                                                      timeout=30,
                                                      check=check)
            except asyncio.TimeoutError:
                pass
            else:
                if str(reaction) == "\N{THUMBS UP SIGN}":
                    # The user wants to continue with the ping
                    await self.staff_role.edit(mentionable=True)
                    staff_ping = Embed(
                        title="This user has requested an official staff ping!",
                        colour=0xFF0000,
                        description=message.content,
                    ).set_author(
                        name=
                        f"{message.author.name}#{message.author.discriminator}",
                        icon_url=message.author.avatar_url,
                    )
                    # Send the embed with the user's content
                    await message.channel.send(self.staff_role.mention,
                                               embed=staff_ping)
                    await self.staff_role.edit(mentionable=False)
                    # Delete the original message
                    await message.delete()
            finally:
                await sent.delete()

        ctx = await self.bot.get_context(message)

        if ctx.valid:
            # Don't react to valid commands
            return

        for word in message.content.lower().split():
            # Check if the message contains a trigger
            if word in REACT_TRIGGERS:
                to_react = REACT_TRIGGERS[word]

                if len(to_react) > 1:  # We have a string to react with
                    await emojify(message, to_react)
                else:
                    await message.add_reaction(to_react)

                return  # Only one auto-reaction per message

        # Adds waving emoji when a new user joins.
        if "Welcome to the Cyber Discovery" in message.content and message.author.id == WELCOME_BOT_ID:
            await message.add_reaction("\N{WAVING HAND SIGN}")
Beispiel #11
0
 def embed(self):
     return Embed(title="Error", description=self.msg, color=Color.red())
Beispiel #12
0
    async def paginate(
        cls,
        lines: t.List[str],
        ctx: Context,
        embed: discord.Embed,
        prefix: str = "",
        suffix: str = "",
        max_lines: t.Optional[int] = None,
        max_size: int = 500,
        scale_to_size: int = 4000,
        empty: bool = True,
        restrict_to_user: User = None,
        timeout: int = 300,
        footer_text: str = None,
        url: str = None,
        exception_on_empty_embed: bool = False,
    ) -> t.Optional[discord.Message]:
        """
        Use a paginator and set of reactions to provide pagination over a set of lines.

        The reactions are used to switch page, or to finish with pagination.

        When used, this will send a message using `ctx.send()` and apply a set of reactions to it. These reactions may
        be used to change page, or to remove pagination from the message.

        Pagination will also be removed automatically if no reaction is added for five minutes (300 seconds).

        The interaction will be limited to `restrict_to_user` (ctx.author by default) or
        to any user with a moderation role.

        Example:
        >>> embed = discord.Embed()
        >>> embed.set_author(name="Some Operation", url=url, icon_url=icon)
        >>> await LinePaginator.paginate([line for line in lines], ctx, embed)
        """
        paginator = cls(prefix=prefix,
                        suffix=suffix,
                        max_size=max_size,
                        max_lines=max_lines,
                        scale_to_size=scale_to_size)
        current_page = 0

        if not restrict_to_user:
            restrict_to_user = ctx.author

        if not lines:
            if exception_on_empty_embed:
                log.exception("Pagination asked for empty lines iterable")
                raise EmptyPaginatorEmbedError("No lines to paginate")

            log.debug(
                "No lines to add to paginator, adding '(nothing to display)' message"
            )
            lines.append("(nothing to display)")

        for line in lines:
            try:
                paginator.add_line(line, empty=empty)
            except Exception:
                log.exception(f"Failed to add line to paginator: '{line}'")
                raise  # Should propagate
            else:
                log.trace(f"Added line to paginator: '{line}'")

        log.debug(f"Paginator created with {len(paginator.pages)} pages")

        embed.description = paginator.pages[current_page]

        if len(paginator.pages) <= 1:
            if footer_text:
                embed.set_footer(text=footer_text)
                log.trace(f"Setting embed footer to '{footer_text}'")

            if url:
                embed.url = url
                log.trace(f"Setting embed url to '{url}'")

            log.debug(
                "There's less than two pages, so we won't paginate - sending single page on its own"
            )
            return await ctx.send(embed=embed)
        else:
            if footer_text:
                embed.set_footer(
                    text=
                    f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})"
                )
            else:
                embed.set_footer(
                    text=f"Page {current_page + 1}/{len(paginator.pages)}")
            log.trace(f"Setting embed footer to '{embed.footer.text}'")

            if url:
                embed.url = url
                log.trace(f"Setting embed url to '{url}'")

            log.debug("Sending first page to channel...")
            message = await ctx.send(embed=embed)

        log.debug("Adding emoji reactions to message...")

        for emoji in PAGINATION_EMOJI:
            # Add all the applicable emoji to the message
            log.trace(f"Adding reaction: {repr(emoji)}")
            await message.add_reaction(emoji)

        check = partial(
            messages.reaction_check,
            message_id=message.id,
            allowed_emoji=PAGINATION_EMOJI,
            allowed_users=(restrict_to_user.id, ),
        )

        while True:
            try:
                reaction, user = await ctx.bot.wait_for("reaction_add",
                                                        timeout=timeout,
                                                        check=check)
                log.trace(f"Got reaction: {reaction}")
            except asyncio.TimeoutError:
                log.debug("Timed out waiting for a reaction")
                break  # We're done, no reactions for the last 5 minutes

            if str(reaction.emoji) == DELETE_EMOJI:
                log.debug("Got delete reaction")
                return await message.delete()

            if reaction.emoji == FIRST_EMOJI:
                await message.remove_reaction(reaction.emoji, user)
                current_page = 0

                log.debug(
                    f"Got first page reaction - changing to page 1/{len(paginator.pages)}"
                )

                embed.description = paginator.pages[current_page]
                if footer_text:
                    embed.set_footer(
                        text=
                        f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})"
                    )
                else:
                    embed.set_footer(
                        text=f"Page {current_page + 1}/{len(paginator.pages)}")
                await message.edit(embed=embed)

            if reaction.emoji == LAST_EMOJI:
                await message.remove_reaction(reaction.emoji, user)
                current_page = len(paginator.pages) - 1

                log.debug(
                    f"Got last page reaction - changing to page {current_page + 1}/{len(paginator.pages)}"
                )

                embed.description = paginator.pages[current_page]
                if footer_text:
                    embed.set_footer(
                        text=
                        f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})"
                    )
                else:
                    embed.set_footer(
                        text=f"Page {current_page + 1}/{len(paginator.pages)}")
                await message.edit(embed=embed)

            if reaction.emoji == LEFT_EMOJI:
                await message.remove_reaction(reaction.emoji, user)

                if current_page <= 0:
                    log.debug(
                        "Got previous page reaction, but we're on the first page - ignoring"
                    )
                    continue

                current_page -= 1
                log.debug(
                    f"Got previous page reaction - changing to page {current_page + 1}/{len(paginator.pages)}"
                )

                embed.description = paginator.pages[current_page]

                if footer_text:
                    embed.set_footer(
                        text=
                        f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})"
                    )
                else:
                    embed.set_footer(
                        text=f"Page {current_page + 1}/{len(paginator.pages)}")

                await message.edit(embed=embed)

            if reaction.emoji == RIGHT_EMOJI:
                await message.remove_reaction(reaction.emoji, user)

                if current_page >= len(paginator.pages) - 1:
                    log.debug(
                        "Got next page reaction, but we're on the last page - ignoring"
                    )
                    continue

                current_page += 1
                log.debug(
                    f"Got next page reaction - changing to page {current_page + 1}/{len(paginator.pages)}"
                )

                embed.description = paginator.pages[current_page]

                if footer_text:
                    embed.set_footer(
                        text=
                        f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})"
                    )
                else:
                    embed.set_footer(
                        text=f"Page {current_page + 1}/{len(paginator.pages)}")

                await message.edit(embed=embed)

        log.debug("Ending pagination and clearing reactions.")
        with suppress(discord.NotFound):
            await message.clear_reactions()
Beispiel #13
0
    async def log_to_channel(self, ctx: Context, target: Member, info: str = None):
        """Send an embed-formatted log of an event to a channel."""
        sid = str(ctx.guild.id)
        channel = None
        enabled = True
        action = ctx.message.content

        if sid in self.db:
            try:
                channel = ctx.guild.get_channel(int(self.db[sid]["log_channel"]))
            except KeyError:
                channel = None
            try:
                enabled = self.db[sid]["log"]
            except KeyError:
                enabled = False
        else:
            channel = ctx.channel
            enabled = False

        if not enabled:
            return

        if info is None:
            info = "No extra information"

        tag = f"{target.name}#{target.discriminator} ({target.id})"

        embed = Embed(
            title=f"{ctx.author.name}#{ctx.author.discriminator} {ctx.command.name}",
            color=0xFF0000,
        )
        embed.set_thumbnail(url=str(ctx.author.avatar_url))
        embed.add_field(name="Action", value=action, inline=False)
        embed.add_field(name="Target", value=tag)
        embed.add_field(name="Info", value=info)
        embed.set_footer(text=pretty_datetime(datetime.now()))

        await channel.send(embed=embed)
Beispiel #14
0
async def embed_builder(
    action: str, member: Member, reason: str, td: timedelta = None
) -> Embed:
    embed = Embed(title=action, color=0xFF0000)

    embed.add_field(name="From", value=member.guild.name)
    embed.add_field(name="Reason", value=reason, inline=False)

    try:
        embed.set_author(icon_url=member.guild.icon)
    except Exception:
        pass

    if td is not None:
        embed.add_field(name="Expires In", value=pretty_timedelta(td), inline=False)

    embed.set_footer(text=pretty_datetime(datetime.now()))

    return embed
Beispiel #15
0
async def on_command_error(ctx, error):
    error_embed = Embed(title='Error', color=0xff0000)
    error_embed.description = str(error)
    if isinstance(error, MissingPermissions):
        error_embed.description = 'You do not have the permissions to run this command'
    elif isinstance(error, MissingRequiredArgument):
        error_embed.description = f'Missing required Argument: `{str(error).split()[0]}`'
    elif isinstance(error, CommandNotFound):
        error = str(error).split()[1].strip('"')
        error_embed.description = f'Command not found: `{error}`'
    elif isinstance(error, CommandOnCooldown):
        error_embed.description = 'This Command is still on a cooldown'
    elif isinstance(error, NotOwner):
        error_embed.description = 'You can not run this command because you are not the owner of this bot'
    elif isinstance(error, BadBoolArgument):
        error_embed.description = 'Your given argument can not be converted to a boolean datatype'
    elif isinstance(error, NSFWChannelRequired):
        error_embed.description = 'This command is only for use in NSFW Channels'
    elif isinstance(error, NoPrivateMessage):
        error_embed.description = 'The user is not accepting private messages from guild members'
    elif isinstance(error, BotMissingPermissions):
        error_embed.description = 'The user is not accepting private messages from guild members'
    await ctx.send(embed=error_embed)
Beispiel #16
0
    async def ranking(self, ctx): #既に存在する関数名だったらERROR出るのでもし今後コマンドを追加するならコマンド名と同じ関数名にして下さい。
        f"""
        各種ランキングの表示
        各種100位まで表示するようにしております。
        10位ごとに勝手にページが分けられます。
        f"""
        try: # ERRORが起きるか起きないか。起きたらexceptに飛ばされる
            bot = self.bot
            r_dict = {'0⃣': "プレイヤーランキング", '1⃣': "BOTランキング",  '2⃣': "鯖ランキング"}
          # sqlite_listの中からデータベースに接続したというオブジェクトを取得
            _, conn, cur = [sql for sql in self.bot.sqlite_list if ctx.author.id == sql[0]][0]
            msg = await ctx.send(embed=Embed(description="\n".join([f"{r[0]}:`{r[1]}`" for r in list(r_dict.items())]) + "\n見たい番号を発言してください。").set_author(name="Ranking一覧:"))
            msg_react = await self.bot.wait_for('message', check=lambda message: message.author == ctx.author and message.content.isdigit() and 0 <= int(message.content) <= len(list(r_dict.keys())) - 1, timeout=10)
          # await self.bot.wait_for('message')で返ってくるのは文字列型
            if msg_react.content == "0":
              # ユーザーはisbotの中身を0で登録してるのでそこで判断して全データを取得させます。
                await cur.execute("select distinct user_id, exp FROM player WHERE isbot =0 ORDER BY exp DESC;")
                players_rank = "\n".join(["{0}位:[`{1}`] (Lv{2})".format(k, bot.get_user(member[0]), int(math.sqrt(member[1]))) for member, k in zip(await cur.fetchall(), range(1, 101))])
              # データ10個ごとにページ分け
                ranking_msgs = ["\n".join(players_rank.split("\n")[i:i + 10]) for i in range(0, 100, 10)]
                author = "世界Top100プレイヤー"
            elif msg_react.content == "1":
              # BOTはisbotの中身を1で登録してるのでそこで判断して全データを取得させます。
                await cur.execute("select distinct user_id, exp FROM player WHERE isbot=1 ORDER BY exp DESC;")
                players_rank = "\n".join(["{0}位:[`{1}`] (Lv{2})".format(k, bot.get_user(member[0]), int(math.sqrt(member[1]))) for member, k in zip(await cur.fetchall(), range(1, 101))])
              # データ10個ごとにページ分け
                ranking_msgs = ["\n".join(players_rank.split("\n")[i:i + 10]) for i in range(0, 100, 10)]
                author = "世界Top100ボット"
            else:
                server_id = []
              # チャンネルのレベル(昇順)にリストを取得します
              # if not [cc for cc in server_id if c[0] in cc] => 既に鯖がリストに入ってる場合は無視するための処理です
              # bot.get_guild(c[0]) => その鯖をBOTが取得できるかの処理です。取得できなかった場合はその鯖の情報は無視されます。
                await cur.execute('select distinct server_id, boss_level FROM channel_status ORDER BY boss_level DESC;')
                [server_id.append(c) for c in await cur.fetchall() if not [cc for cc in server_id if c[0] in cc] and bot.get_guild(c[0])]
                players_rank = "".join(["{0}位:[`{1}`] (Lv{2})\n".format(k, bot.get_guild(c[0]), c[1]) for c, k in zip(server_id, range(1, 101))])
              # データ10個ごとにページ分け
                ranking_msgs = ["\n".join(players_rank.split("\n")[i:i + 10]) for i in range(0, 100, 10)]
                author = "世界Top100サーバー"

            if not list(filter(lambda a: a != '', ranking_msgs)):
                return await ctx.send(embed=Embed(description="まだデータはないようだ..."))

            embeds = []
            for embed in list(filter(lambda a: a != '', ranking_msgs)):
                embeds.append(Embed(description=embed).set_author(name=author))

            await msg.edit(content=f"```diff\n1ページ/{len(embeds)}ページ目を表示中\n見たいページを発言してください。\n30秒経ったら処理は止まります。\n0と発言したら強制的に処理は止まります。```", embed=embeds[0])
            while True: # 処理が終わる(return)まで無限ループ
                try: # ERRORが起きるか起きないか。起きたらexceptに飛ばされる
                    msg_react = await self.bot.wait_for('message', check=lambda m: m.author == ctx.author and m.content.isdigit() and 0 <= int(m.content) <= len(embeds), timeout=30)
                  # await self.bot.wait_for('message')で返ってくるのは文字列型
                    if msg_react.content == "0":
                      # このcontentの中にはゼロ幅スペースが入っています。Noneでもいいのですが編集者はこっちの方が分かりやすいからこうしています。
                        return await msg.edit(content="‌")
                    await msg.edit(content=f"```diff\n{int(msg_react.content)}ページ/{len(embeds)}ページ目を表示中\n見たいページを発言してください。\n30秒経ったら処理は止まります。\n0と発言したら強制的に処理は止まります。```", embed=embeds[int(msg_react.content)-1])
                except asyncio.TimeoutError: # wait_forの時間制限を超過した場合
                  # このcontentの中にはゼロ幅スペースが入っています。Noneでもいいのですが編集者はこっちの方が分かりやすいからこうしています。
                    return await msg.edit(content="‌", embed=Embed(title=f"時間切れです..."))

        except (NotFound, asyncio.TimeoutError, Forbidden): # 編集した際に文字が見つからなかった, wait_forの時間制限を超過した場合, メッセージに接続できなかった
            return
        except: # 上のERROR以外のERROR出た場合はtracebackで表示するようにしています。 上手くコマンドが反応しない場合はコンソールを見てね!
            return print("エラー情報\n" + traceback.format_exc())
Beispiel #17
0
    async def status(self, ctx): #既に存在する関数名だったらERROR出るのでもし今後コマンドを追加するならコマンド名と同じ関数名にして下さい。
        f"""
        ステータスの表示。
        今後アイテム追加した場合に備えてページ分けを自動でさせてます。
        f"""
        try: # ERRORが起きるか起きないか。起きたらexceptに飛ばされる
          # sqlite_listの中からデータベースに接続したというオブジェクトを取得
            _, conn, cur = [sql for sql in self.bot.sqlite_list if ctx.author.id == sql[0]][0]
            user = ctx.author
            user_id = ctx.author.id
            user_name = ctx.author.nick if ctx.author.nick else ctx.author.name

          # ユーザーの総経験値を取得
            player_exp = await database.get_player_exp(ctx, user_id, conn, cur)

          # 戦闘チャンネルの確認
            await cur.execute("select distinct channel_id FROM in_battle WHERE user_id=?", (user_id,))
            in_battle = await cur.fetchone()
            battle_str = f"{self.bot.get_channel(in_battle[0]).guild.name}の{self.bot.get_channel(in_battle[0]).mention}" if in_battle and self.bot.get_channel(in_battle[0]) else "現在休戦中or認識出来ないチャンネル"

          # アイテムを所持してるかの確認。アイテムはitem_idで登録してるので名前をall_dataファイルのitem_lists変数から引っ張ってきます。
            await cur.execute("select distinct item_id,count FROM item WHERE user_id=? ORDER BY item_id;", (user_id,))
            i_list = ''.join(f'{item_lists[i[0]]} : {i[1]}個\n' for i in await cur.fetchall())
          # 今後アイテムが増えた場合に25個のデータ(重複なし)でページ分け
            msgs = list(filter(lambda a: a != "", ["\n".join(i_list.split("\n")[i:i + 25]) for i in range(0, len(i_list), 25)] if i_list != "" else ["無し"]))

          # 現在のユーザのランキングlistで返しindexで順位を取得する方法にしています。
            await cur.execute("select distinct user_id FROM player ORDER BY exp DESC;")
            user_rank = [r[0] for r in await cur.fetchall()]

          # 経験値をmath.sqrt(平方根の計算)を使用し求めます。整数を返すためにint()を使用しています。
            player_level = int(math.sqrt(player_exp))
            embeds = []
            embed = Embed()
            embed.add_field(name="Lv", value=str(player_level))
            embed.add_field(name="HP", value=str(player_level*5+50))
            embed.add_field(name="ATK", value=str(int(player_level*5+50)))
            embed.add_field(name="EXP", value=str(player_exp))
          # レベル+1の必要経験値数(2乗)-現在の経験値数で計算しています。
            embed.add_field(name="次のLvまで", value=str((player_level+1)**2-player_exp)+"exp")
            embed.add_field(name="戦闘状況:", value=battle_str)
            embed.add_field(name="順位:", value=f"{user_rank.index(user_id) + 1}位")
          # ユーザーのアイコン表示
            embed.set_thumbnail(url=user.avatar_url_as())
            embed.set_author(name=f"{user_name}のステータス:")
            embeds.append(embed)

            [embeds.append(Embed(description=f"```{i if i else 'アイテムを所持していません。'}```").set_thumbnail(url=user.avatar_url_as()).set_author(name=f"{user_name}のステータス:")) for i in msgs]

            msg = await ctx.send(content=f"```diff\n1ページ/{len(embeds)}ページ目を表示中\n見たいページを発言してください。\n30秒経ったら処理は止まります。\n0と発言したら強制的に処理は止まります。```", embed=embeds[0])
            while True: # 処理が終わる(return)まで無限ループ
                try: # ERRORが起きるか起きないか。起きたらexceptに飛ばされる
                    msg_react = await self.bot.wait_for('message', check=lambda m: m.author == ctx.author and m.content.isdigit() and 0 <= int(m.content) <= len(embeds), timeout=30)
                  # await self.bot.wait_for('message')で返ってくるのは文字列型
                    if msg_react.content == "0":
                      # このcontentの中にはゼロ幅スペースが入っています。Noneでもいいのですが編集者はこっちの方が分かりやすいからこうしています。
                        return await msg.edit(content="‌")
                    await msg.edit(content=f"```diff\n{int(msg_react.content)}ページ/{len(embeds)}ページ目を表示中\n見たいページを発言してください。\n30秒経ったら処理は止まります。\n0と発言したら強制的に処理は止まります。```", embed=embeds[int(msg_react.content)-1])
                except asyncio.TimeoutError: # wait_forの時間制限を超過した場合
                  # このcontentの中にはゼロ幅スペースが入っています。Noneでもいいのですが編集者はこっちの方が分かりやすいからこうしています。
                    return await msg.edit(content="‌", embed=Embed(title=f"時間切れです..."))

        except (NotFound, asyncio.TimeoutError, Forbidden): # 編集した際に文字が見つからなかった, wait_forの時間制限を超過した場合, メッセージに接続できなかった
            return
        except: # 上のERROR以外のERROR出た場合はtracebackで表示するようにしています。 上手くコマンドが反応しない場合はコンソールを見てね!
            return print("エラー情報\n" + traceback.format_exc())
    async def ln(self, args, mobj):
        try:
            """
            Does a AniList search to find requested Light Novel.
            
            If there are multiple options, will ask you which one to choose. You will have 10 seconds to respond.
            
            Will not return any adult series.
            """
            args = " ".join(args)
            result = await self.ani_get_options(args, mobj, 'LN')
            if result:
                manga = self.anilist.getMangaDetailsById(result)
            else:
                manga = self.anilist.getMangaDetails(args)

            if not manga:
                await self.error(mobj.channel, "Could not find anything")
                return
            embed = Embed(
                title=manga['title_english']
                if manga['title_english'] else manga['title_romaji']
                if manga['title_romaji'] else manga['title_japanese'],
                colour=Color(0x7289da),
                url=
                f"https://anilist.co/manga/{manga['id']}/{manga['title_romaji']}"
                .replace(" ", "%20"))
            # embed.set_author(name=author.display_name, icon_url=avatar)
            embed.set_image(url=manga['img'])
            embed.add_field(
                name="Length",
                value=
                f'{manga["total_chapters"] if manga["total_chapters"] else 0} Chapters, {manga["total_volumes"] if manga["total_volumes"] else 0} Volumes'
            )
            embed.add_field(name="Type", value=manga["type"])
            embed.add_field(name="Status", value=manga["airing_status"])
            embed.add_field(
                name="Dates",
                value=
                f'{manga["start_date"]} through {manga["end_date"] if manga["end_date"] != "None-None-None" else "present"}'
                if manga["start_date"] != manga["end_date"] else
                f'{manga["start_date"]}')

            if manga["synonyms"]:
                embed.add_field(name="Synonyms",
                                value=", ".join(manga["synonyms"]))

            if manga["description"] is None:
                manga["description"] = "Could not pull synopsis"
            manga["description"] = re.sub(
                r'\n\s*\n', '\n\n',
                unescape(manga["description"]).replace("<br>", "\n"))
            if len(manga["description"]) > 1000:
                if "\n" in manga["description"][:965]:
                    manga["description"] = manga[
                        "description"][:manga["description"][:965].rfind("\n")]
                else:
                    manga["description"] = manga["description"][:965]
                manga["description"] += "\nIncomplete synopsis due to length."

            embed.add_field(name="Synopsis", value=manga["description"])

            try:
                await self.embed(mobj.channel, embed)
            except BaseException:
                await self.error(
                    mobj.channel,
                    "Something when trying to format the object. Here is a link to the manga: "
                    + "https://myanimelist.net/manga/{0.id}/{0.title}".format(
                        manga).replace(" ", "%20"))
        except Exception as e:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            print(exc_type, fname, exc_tb.tb_lineno)
    async def anime(self, args, mobj):
        """
        Does a AniList search to find requested Anime
        
        If there are multiple options, will ask you which one to choose. You will have 10 seconds to respond.
        
        Will not return any adult series.
        """
        args = " ".join(args)

        result = await self.ani_get_options(args, mobj, 'ANIME')

        if result:
            anime = self.anilist.getAnimeDetailsById(result)
        else:
            anime = self.anilist.getAnimeDetails(args)
        if not anime:
            await self.error(mobj.channel, "Could not find anything")
            return

        if not anime:
            await self.error(mobj.channel, "Could not find anything")
            return

        embed = Embed(
            title=anime['title_english']
            if anime['title_english'] else anime['title_romaji']
            if anime['title_romaji'] else anime['title_japanese'],
            colour=Color(0x7289da),
            url=f"https://anilist.co/anime/{anime['id']}/{anime['title_romaji']}"
            .replace(" ", "%20"))
        embed.set_image(url=anime['img'])
        embed.add_field(name="Episode Count",
                        value=str(anime['total_episodes']))
        embed.add_field(name="Type", value=anime['type'])
        embed.add_field(name="Status", value=anime['airing_status'])

        embed.add_field(
            name="Dates",
            value=
            f'{anime["start_date"]} through {anime["end_date"] if anime["end_date"] != "None-None-None" else "present"}'
            if anime["start_date"] != anime["end_date"] else
            f'{anime["start_date"]}')

        if anime["synonyms"]:
            embed.add_field(name="Synonyms",
                            value=", ".join(anime["synonyms"]))
        if anime["description"] is None:
            anime["description"] = "Could not pull synopsis"
        anime["description"] = re.sub(
            r'\n\s*\n', '\n\n',
            unescape(anime["description"]).replace("<br>", "\n"))
        if len(anime["description"]) > 1000:
            if "\n" in anime["description"][:965]:
                anime["description"] = anime[
                    "description"][:anime["description"][:965].rfind("\n")]
            else:
                anime["description"] = anime["description"][:965]
            anime["description"] += "\nIncomplete synopsis due to length."

        embed.add_field(name="Synopsis", value=anime["description"])
        try:
            await self.embed(mobj.channel, embed)
        except BaseException:
            await self.error(
                mobj.channel,
                "Something when trying to format the object. Here is a link to the anime: "
                + "https://myanimelist.net/anime/{0.id}/{0.title}".format(
                    anime).replace(" ", "%20"))
Beispiel #20
0
async def _inspire(ctx: Context):
    embed = Embed()
    embed.set_image(
        url=requests.get("https://inspirobot.me/api?generate=true").text)
    return await ctx.send(embed=embed)
Beispiel #21
0
    async def get_command(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:
        """Get a specified tag, or a list of all tags if no tag is specified."""
        def _command_on_cooldown(tag_name: str) -> bool:
            """
            Check if the command is currently on cooldown, on a per-tag, per-channel basis.

            The cooldown duration is set in constants.py.
            """
            now = time.time()

            cooldown_conditions = (
                tag_name
                and tag_name in self.tag_cooldowns
                and (now - self.tag_cooldowns[tag_name]["time"]) < Cooldowns.tags
                and self.tag_cooldowns[tag_name]["channel"] == ctx.channel.id
            )

            if cooldown_conditions:
                return True
            return False

        if _command_on_cooldown(tag_name):
            time_left = Cooldowns.tags - (time.time() - self.tag_cooldowns[tag_name]["time"])
            log.info(
                f"{ctx.author} tried to get the '{tag_name}' tag, but the tag is on cooldown. "
                f"Cooldown ends in {time_left:.1f} seconds."
            )
            return

        await self._get_tags()

        if tag_name is not None:
            founds = await self._get_tag(tag_name)

            if len(founds) == 1:
                tag = founds[0]
                if ctx.channel.id not in TEST_CHANNELS:
                    self.tag_cooldowns[tag_name] = {
                        "time": time.time(),
                        "channel": ctx.channel.id
                    }
                await ctx.send(embed=Embed.from_dict(tag['embed']))
            elif founds and len(tag_name) >= 3:
                await ctx.send(embed=Embed(
                    title='Did you mean ...',
                    description='\n'.join(tag['title'] for tag in founds[:10])
                ))

        else:
            tags = self._cache.values()
            if not tags:
                await ctx.send(embed=Embed(
                    description="**There are no tags in the database!**",
                    colour=Colour.red()
                ))
            else:
                embed: Embed = Embed(title="**Current tags**")
                await LinePaginator.paginate(
                    sorted(f"**»**   {tag['title']}" for tag in tags),
                    ctx,
                    embed,
                    footer_text="To show a tag, type !tags <tagname>.",
                    empty=False,
                    max_lines=15
                )
Beispiel #22
0
    async def info(self, ctx):
        """
        send an embed containing info in format
        Server_id           Owner

        Channels
        text | voice
        Total:

        Members
        online | idle | dnd | streaming | offline
        Total:
        """

        status = {}
        for member in ctx.guild.members:
            status[member.status.name] = status.get(member.status.name, 0) + 1

        online = core.utils.get(self.bot.emojis, name="status_online")
        idle = core.utils.get(self.bot.emojis, name="status_idle")
        dnd = core.utils.get(self.bot.emojis, name="status_dnd")
        streaming = core.utils.get(self.bot.emojis, name="status_streaming")
        offline = core.utils.get(self.bot.emojis, name="status_offline")

        text = core.utils.get(self.bot.emojis, name="text_channel")
        voice = core.utils.get(self.bot.emojis, name="voice_channel")

        embed = Embed(
            title=f"{ctx.guild.name}",
            description=
            f"{ctx.guild.description if ctx.guild.description else ''}",
            color=Color.from_rgb(0, 0, 0))
        embed.add_field(name="ID", value=(f"{ctx.guild.id}"))
        embed.add_field(name="Owner", value=(f"{ctx.guild.owner}"))
        embed.add_field(
            name="Channels",
            value=("{text} {text_count} " + "{voice} {voice_count}").format(
                text=text,
                text_count=len(ctx.guild.text_channels),
                voice=voice,
                voice_count=len(ctx.guild.voice_channels)) +
            f"\n**Total:** {len(ctx.guild.channels)}",
            inline=False)
        embed.add_field(
            name="Members",
            value=("{online} {online_count} " + "{idle} {idle_count} " +
                   "{dnd} {dnd_count} " + "{streaming} {streaming_count} " +
                   "{offline} {offline_count}").format(
                       online=online,
                       online_count=status.get("online", 0),
                       idle=idle,
                       idle_count=status.get("idle", 0),
                       dnd=dnd,
                       dnd_count=status.get("dnd", 0),
                       streaming=streaming,
                       streaming_count=status.get("streaming", 0),
                       offline=offline,
                       offline_count=status.get("offline", 0)) +
            f"\n**Total:** {len(ctx.guild.members)}",
            inline=False)

        await ctx.send(embed=embed)
Beispiel #23
0
    async def on_message(self, msg: Message):
        # check if channel is in blacklist, has possible log urls, or an attachment
        if msg.channel.id in self.channel_blacklist:
            return
        if not msg.attachments and not any(lh in msg.content for lh in self._log_hosts):
            return

        # list of candidate tuples consisting of (raw_url, web_url)
        log_candidates = []
        # message attachments
        for attachment in msg.attachments:
            if attachment.url.endswith('.txt'):
                # collisions are possible here, but unlikely, we'll see if it becomes a problem
                if not self.limiter.is_limited(attachment.filename):
                    log_candidates.append((attachment.url, attachment.url))
                else:
                    logger.debug(f'{str(msg.author)} attempted to upload a rate-limited log.')

        # links in message
        for part in [p.strip() for p in msg.content.split()]:
            if any(part.startswith(lh) for lh in self._log_hosts):
                if 'obsproject.com' in part:
                    raw_url = html_url = part
                elif 'hastebin.com' in part:
                    hastebin_id = part.rsplit('/', 1)[1]
                    if not hastebin_id:
                        continue
                    raw_url = f'https://hastebin.com/raw/{hastebin_id}'
                    html_url = f'https://hastebin.com/{hastebin_id}'
                elif 'pastebin.com' in part:
                    pastebin_id = part.rsplit('/', 1)[1]
                    if not pastebin_id:
                        continue
                    raw_url = f'https://pastebin.com/raw/{pastebin_id}'
                    html_url = f'https://pastebin.com/{pastebin_id}'
                else:
                    continue

                if self.bot.is_supporter(msg.author) or not self.limiter.is_limited(raw_url):
                    log_candidates.append((raw_url, html_url))
                else:
                    logger.debug(f'{str(msg.author)} attempted to post a rate-limited log.')

        if not log_candidates:
            return

        if len(log_candidates) > 3:
            logger.warning('There are too many possible log URLs, cutting down to 3...')
            log_candidates = log_candidates[:3]

        async with msg.channel.typing():
            for raw_url, html_url in log_candidates:
                # download log for local analysis
                try:
                    log_content = await self.download_log(raw_url)
                except ValueError:  # not a valid OBS log
                    continue
                except ClientResponseError:  # file download failed
                    logger.error(f'Failed retrieving log from "{raw_url}"')
                    continue
                except Exception as e:  # catch everything else
                    logger.error(f'Unhandled exception when downloading log: {repr(e)}')
                    continue

                # fetch log analysis from OBS analyser
                try:
                    log_analysis = await self.fetch_log_analysis(raw_url)
                except ClientResponseError:  # file download failed
                    logger.error(f'Failed retrieving log analysis from "{raw_url}"')
                    continue
                except Exception as e:  # catch everything else
                    logger.error(f'Unhandled exception when analysing log: {repr(e)}')
                    continue

                # check if analysis json is actually valid
                if not all(i in log_analysis for i in ('critical', 'warning', 'info')):
                    logger.error(f'Analyser result for "{raw_url}" is invalid.')
                    continue

                anal_url = f'https://obsproject.com/tools/analyzer?log_url={urlencode(html_url)}'
                embed = Embed(colour=Colour(0x5a7474), url=anal_url)

                def pretty_print_messages(msgs):
                    ret = []
                    for _msg in msgs:
                        ret.append(f'- {_msg}')
                    return '\n'.join(ret)

                if log_analysis['critical']:
                    embed.add_field(name="🛑 Critical",
                                    value=pretty_print_messages(log_analysis['critical']))
                if log_analysis['warning']:
                    embed.add_field(name="⚠️ Warning",
                                    value=pretty_print_messages(log_analysis['warning']))
                if log_analysis['info']:
                    embed.add_field(name="ℹ️ Info",
                                    value=pretty_print_messages(log_analysis['info']))

                # do local hardware check/stats collection and include results if enabled
                hw_results = await self.match_hardware(log_content)
                if self.bot.state.get('hw_check_enabled', False):
                    if hardware_check_msg := self.hardware_check(hw_results):
                        embed.add_field(name='Hardware Check', inline=False,
                                        value=' / '.join(hardware_check_msg))

                embed.add_field(name='Analyser Report', inline=False,
                                value=f'[**Click here for solutions / full analysis**]({anal_url})')

                # include filtered log in case SE or FTL spam is detected
                if 'obsproject.com' in raw_url and any(elem in log_content for
                                                       elem in self._filtered_log_needles):
                    clean_url = raw_url.replace('obsproject.com', 'obsbot.rodney.io')
                    embed.description = f'*Log contains debug messages (browser/ftl/etc), ' \
                                        f'for a filtered version [click here]({clean_url})*\n'

                return await msg.channel.send(embed=embed, reference=msg, mention_author=True)
Beispiel #24
0
    async def pep_command(self, ctx: Context, pep_number: str):
        """
        Fetches information about a PEP and sends it to the channel.
        """

        if pep_number.isdigit():
            pep_number = int(pep_number)
        else:
            return await ctx.invoke(self.bot.get_command("help"), "pep")

        # Newer PEPs are written in RST instead of txt
        if pep_number > 542:
            pep_url = f"{self.base_github_pep_url}{pep_number:04}.rst"
        else:
            pep_url = f"{self.base_github_pep_url}{pep_number:04}.txt"

        # Attempt to fetch the PEP
        log.trace(f"Requesting PEP {pep_number} with {pep_url}")
        response = await self.bot.http_session.get(pep_url)

        if response.status == 200:
            log.trace("PEP found")

            pep_content = await response.text()

            # Taken from https://github.com/python/peps/blob/master/pep0/pep.py#L179
            pep_header = HeaderParser().parse(StringIO(pep_content))

            # Assemble the embed
            pep_embed = Embed(
                title=f"**PEP {pep_number} - {pep_header['Title']}**",
                description=f"[Link]({self.base_pep_url}{pep_number:04})",
            )

            pep_embed.set_thumbnail(
                url="https://www.python.org/static/opengraph-icon-200x200.png")

            # Add the interesting information
            if "Status" in pep_header:
                pep_embed.add_field(name="Status", value=pep_header["Status"])
            if "Python-Version" in pep_header:
                pep_embed.add_field(name="Python-Version",
                                    value=pep_header["Python-Version"])
            if "Created" in pep_header:
                pep_embed.add_field(name="Created",
                                    value=pep_header["Created"])
            if "Type" in pep_header:
                pep_embed.add_field(name="Type", value=pep_header["Type"])

        elif response.status == 404:
            log.trace("PEP was not found")
            not_found = f"PEP {pep_number} does not exist."
            pep_embed = Embed(title="PEP not found", description=not_found)
            pep_embed.colour = Colour.red()

        else:
            log.trace(
                f"The user requested PEP {pep_number}, but the response had an unexpected status code: "
                f"{response.status}.\n{response.text}")

            error_message = "Unexpected HTTP error during PEP search. Please let us know."
            pep_embed = Embed(title="Unexpected error",
                              description=error_message)
            pep_embed.colour = Colour.red()

        await ctx.message.channel.send(embed=pep_embed)
Beispiel #25
0
    async def steam(self, ctx):
        """
        1: [steam reference] ie. steamID, steamID3, steamID64, a customURL or a full URL
        2: [result only] name of element to return ie. steamid or profile url (optional)

        Some valid examples:

        a steamID               	STEAM_0:0:11101
        a steamID3	                [U:1:22202]
        a steamID3 without brackets	U:1:22202
        a steamID64	                76561197960287930
        a customURL	                gabelogannewell
        a full URL	                https://steamcommunity.com/profiles/76561197960287930
        a full URL with customURL	https://steamcommunity.com/id/gabelogannewell
        """

        one_message = False
        result_only = None

        try:
            com = ctx.message.content.split(" ")
            steam_reference = ""
            is_admin = (ctx.message.author.permissions_in(
                ctx.message.channel).kick_members)

            if len(com) <= 1:
                if not one_message:
                    await ctx.bot.send_message(ctx.message.channel,
                                               "> No steam reference given.")
                    one_message = True

            elif len(com) == 2:
                steam_reference = com[1]

            elif len(com) >= 3:
                steam_reference = com[1]
                result_only = ' '.join(com[2:])

            # Clean steam reference here also, just to get the correct output later on.
            steam_reference = clean_steam_reference(steam_reference)

            result = get_profile_by_steam(
                steam_reference, is_admin if not result_only else False)

            if result:
                icon = result["avatar"]
                del result["avatar"]

                embed = Embed(color=0xd6c8ff)
                embed.set_author(name=get_title_for_box(
                    steam_reference, result["profile_name"]),
                                 url=result["profile_url"],
                                 icon_url=icon)
                embed.set_footer(
                    text="Results provided by Valve. Author: 4ppl3#0018")

                if not result_only:
                    for kn in result.keys():
                        if result[kn] != "None":
                            embed.add_field(name=kn.upper().replace("_", " "),
                                            value=result[kn])
                        else:
                            continue

                elif result_only:
                    opt = list(result.keys())
                    matches = get_close_matches(result_only, opt)
                    if len(matches) >= 1:
                        kn = matches[0]
                        embed.add_field(name=kn.upper().replace("_", " "),
                                        value=result[kn])

                        if not one_message:
                            await ctx.bot.send_message(ctx.message.channel,
                                                       embed=embed)
                            one_message = True
                    else:
                        if not one_message:
                            await ctx.bot.send_message(
                                ctx.message.channel, "> No such return as '" +
                                result_only + "' in this search.")
                            one_message = True

                if not one_message:
                    await ctx.bot.send_message(ctx.message.channel,
                                               embed=embed)
                    one_message = True

                # CHECK FOR VAC BANS SEPERATE OF THE ONE MESSAGE LOOP
                # Check for vac bans
                bans = get_bans_by_int64(result["steamid64"])
                vac_embed = Embed()

                if bans["VACBanned"] or bans["NumberOfGameBans"]:
                    days_since_last = bans["DaysSinceLastBan"]
                    amount_of_vac = bans["NumberOfVACBans"]
                    amount_of_game = bans["NumberOfGameBans"]
                    vac_word = "ban" if amount_of_vac == 1 else "bans"
                    game_word = "ban" if amount_of_game == 1 else "bans"

                    if amount_of_game:
                        vac_embed.title = "WARNING {} has {} game {} on record! ".format(
                            result["profile_name"], amount_of_game, game_word)
                        vac_embed.colour = 0xffff00

                    if bans["VACBanned"]:
                        vac_embed.title = "WARNING {} has {} VAC {}".format(
                            result["profile_name"], amount_of_vac,
                            vac_word) + (" and {} game {} on record!".format(
                                amount_of_game, game_word)
                                         if amount_of_game else " on record!")
                        vac_embed.colour = 0xff0000

                    vac_embed.set_thumbnail(
                        url=
                        "https://cdn2.iconfinder.com/data/icons/freecns-cumulus/32/519791-101_Warning-512.png"
                    )

                    vac_embed.add_field(name="VAC bans",
                                        value=str(amount_of_vac),
                                        inline=True)
                    vac_embed.add_field(name="Game bans",
                                        value=str(amount_of_game),
                                        inline=True)
                    vac_embed.add_field(name="Days since last ban",
                                        value=str(days_since_last),
                                        inline=False)

                    await ctx.bot.send_message(ctx.message.channel,
                                               embed=vac_embed)

            else:
                if not one_message:
                    await ctx.bot.send_message(
                        ctx.message.channel,
                        "> Steam reference could not be found.")
                    one_message = True

        except Exception as e:
            print(e)
            if not one_message:
                await ctx.bot.send_message(
                    ctx.message.channel,
                    "> Failed to load steam api. See console for error dump.")
                one_message = True

        await self.bot.delete_message(ctx.message)  # delete message when done
Beispiel #26
0
    async def charinfo(self, ctx, *, characters: str):
        """
        Shows you information on up to 25 unicode characters.
        """

        match = re.match(r"<(a?):(\w+):(\d+)>", characters)
        if match:
            embed = Embed(
                title="Non-Character Detected",
                description=
                ("Only unicode characters can be processed, but a custom Discord emoji "
                 "was found. Please remove it and try again."))
            embed.colour = Colour.red()
            return await ctx.send(embed=embed)

        if len(characters) > 25:
            embed = Embed(title=f"Too many characters ({len(characters)}/25)")
            embed.colour = Colour.red()
            return await ctx.send(embed=embed)

        def get_info(char):
            digit = f"{ord(char):x}"
            if len(digit) <= 4:
                u_code = f"\\u{digit:>04}"
            else:
                u_code = f"\\U{digit:>08}"
            url = f"https://www.compart.com/en/unicode/U+{digit:>04}"
            name = f"[{unicodedata.name(char, '')}]({url})"
            info = f"`{u_code.ljust(10)}`: {name} - {char}"
            return info, u_code

        charlist, rawlist = zip(*(get_info(c) for c in characters))

        embed = Embed(description="\n".join(charlist))
        embed.set_author(name="Character Info")

        if len(characters) > 1:
            embed.add_field(name='Raw',
                            value=f"`{''.join(rawlist)}`",
                            inline=False)

        await ctx.send(embed=embed)
Beispiel #27
0
    async def xkcd(self, ctx: Context, number: str = None):
        """
        Fetches xkcd comics.
        If number is left blank, automatically fetches the latest comic.
        If number is set to '?', a random comic is fetched.
        """

        # Creates endpoint URI
        if number is None or number == "?":
            endpoint = "https://xkcd.com/info.0.json"
        else:
            endpoint = f"https://xkcd.com/{number}/info.0.json"

        # Fetches JSON data from endpoint
        async with ClientSession() as session:
            async with session.get(endpoint) as response:
                data = await response.json()

        # Updates comic number
        if number == "?":
            number = randint(1, int(data["num"]))  # noqa: B311
            endpoint = f"https://xkcd.com/{number}/info.0.json"
            async with ClientSession() as session:
                async with session.get(endpoint) as response:
                    data = await response.json()
        else:
            number = data["num"]

        # Creates date object (Sorry, but I'm too tired to use datetime.)
        date = f"{data['day']}/{data['month']}/{data['year']}"

        # Creates Rich Embed, populates it with JSON data and sends it.
        comic = Embed()
        comic.title = data["safe_title"]
        comic.set_footer(text=data["alt"])
        comic.set_image(url=data["img"])
        comic.url = f"https://xkcd.com/{number}"
        comic.set_author(name="xkcd",
                         url="https://xkcd.com/",
                         icon_url="https://xkcd.com/s/0b7742.png")
        comic.add_field(name="Number:", value=number)
        comic.add_field(name="Date:", value=date)
        comic.add_field(name="Explanation:",
                        value=f"https://explainxkcd.com/{number}")

        await ctx.send(embed=comic)
Beispiel #28
0
def createUserEmbed(user, lastSeen=None, isActive=None):
    userData = user._object_data
    embedVar = Embed(title=user.nick, description=user.about, color=0x00ff00)
    if (user.profile_image):
        embedVar.set_thumbnail(url=user.profile_image.url)
    else:
        embedVar.set_thumbnail(url="https://cdn.miyako.rocks/iFunny/nopfp.png")
    if (user.cover_image):
        embedVar.set_image(url=user.cover_image.url)
    else:
        embedVar.set_image(url="https://cdn.miyako.rocks/iFunny/nopfp.png")
    embedVar.add_field(name="User ID", value=userData['id'], inline=True)
    embedVar.add_field(name="Post Count",
                       value='{:,}'.format(userData['num']['created']),
                       inline=True)
    embedVar.add_field(name="Rating",
                       value="{level} ({days} Days)".format(
                           level=userData['meme_experience']['rank'],
                           days='{:,}'.format(
                               userData['meme_experience']['days'])),
                       inline=True)
    embedVar.add_field(name="Feature Count",
                       value='{:,}'.format(userData['num']['featured']),
                       inline=True)
    embedVar.add_field(name="Smile Count",
                       value='{:,}'.format(userData['num']['total_smiles']),
                       inline=True)
    embedVar.add_field(name="Subscriber Count",
                       value='{:,}'.format(userData['num']['subscribers']),
                       inline=True)
    embedVar.add_field(name="Subscription Count",
                       value='{:,}'.format(userData['num']['subscriptions']),
                       inline=True)
    embedVar.add_field(name="Verified",
                       value=str(userData['is_verified']),
                       inline=True)
    embedVar.add_field(name="Chat Privacy",
                       value=userData['messaging_privacy_status'].capitalize(),
                       inline=True)
    embedVar.add_field(name="Visit Profile",
                       value="[Click Here](%s)" % userData['web_url'])
    if lastSeen:
        date = datetime.datetime.now()
        embedVar.add_field(name="Last Seen",
                           value="{time} ({ago})".format(
                               time=strftime("%b %d %Y %H:%M:%S",
                                             gmtime(lastSeen / 1000)),
                               ago=timeago.format(lastSeen / 1000, date)))
    if isActive:
        embedVar.add_field(name="Is Active", value=str(isActive), inline=True)
    embedVar.set_footer(text="Bot made by Request#0002")
    return embedVar
    async def _parse_embed(self, ctx, specification, return_todelete=False):
        to_delete = []

        split = specification.split(';', 7)
        nfields = len(split)
        if nfields != 7:
            op = 'many' if nfields > 7 else 'few'
            msg = 'Invalid specification: got too {} fields ({}, expected 7)'
            await self.bot.say(error(msg.format(op, nfields)))
            return

        title, color, ftext, fimage, image, timage, body = map(str.strip, split)

        title_url = extract_md_link(title) or Embed.Empty
        if title_url:
            title, title_url = title_url
            if not is_valid_url(title_url):
                await self.bot.say(error('Invalid title URL!'))
                return

        try:
            color = int(color_converter(color), 16)
        except ValueError as e:
            colorstr = color.lower().replace(' ', '_')
            if colorstr == 'random':
                color = discord.Color(random.randrange(0x1000000))
            elif colorstr.strip() in ('', 'none'):
                color = Embed.Empty
            elif colorstr.strip() == 'black':
                color = discord.Color.default()
            elif hasattr(discord.Color, colorstr):
                color = getattr(discord.Color, colorstr)()
            else:
                await self.bot.say(error(e.args[0]))
                return

        if ftext.lower() in ('none', ''):
            ftext = Embed.Empty

        if fimage.lower() in ('none', ''):
            fimage = Embed.Empty
        elif not is_valid_url(fimage):
            await self.bot.say(error('Invalid footer icon URL!'))
            return

        if image.lower() in ('none', ''):
            image = Embed.Empty
        elif not is_valid_url(image):
            await self.bot.say(error('Invalid image URL!'))
            return

        if timage.lower() in ('none', ''):
            timage = Embed.Empty
        elif not is_valid_url(timage):
            await self.bot.say(error('Invalid thumbnail URL!'))
            return

        if body.lower() == 'prompt':
            msg = await self.bot.say('Post the desired content of your embed, '
                                     ' or "cancel" to cancel. Will wait one'
                                     ' minute.')
            to_delete.append(msg)

            msg = await self.bot.wait_for_message(author=ctx.message.author,
                                                  channel=ctx.message.channel,
                                                  timeout=60)
            if msg is None:
                await self.bot.say(error('Timed out waiting for a reply.'))
                return
            else:
                to_delete.append(msg)

            if msg.content.lower().strip() == 'cancel':
                await self.bot.say(info('Cancelled.'))
                return
            else:
                body = msg.content

        embed = Embed(title=title, color=color, description=body, url=title_url)

        if image:
            embed.set_image(url=image)
        if ftext or fimage:
            embed.set_footer(text=ftext, icon_url=fimage)
        if timage:
            embed.set_thumbnail(url=timage)

        if return_todelete:
            return embed, to_delete
        return embed
Beispiel #30
0
async def issues(msg):
    if msg["action"] in invalid_actions:
        return

    issue = msg["issue"]
    sender = msg["sender"]
    repository = msg["repository"]
    pre = None
    embed = Embed()
    if msg["action"] == "closed":
        pre = "<:ISSclosed:246037286322569216>"
        embed.colour = COLOR_GITHUB_RED
    else:
        pre = "<:ISSopened:246037149873340416>"
        embed.colour = COLOR_GITHUB_GREEN

    embed.title = pre + issue["title"]
    embed.url = issue["html_url"]
    embed.set_author(
        name=sender["login"], url=sender["html_url"], icon_url=sender["avatar_url"])
    embed.set_footer(text="{}#{} by {}".format(
        repository["full_name"], issue["number"], issue["user"]["login"]), icon_url=issue["user"]["avatar_url"])
    if len(issue["body"]) > MAX_BODY_LENGTH:
        embed.description = issue["body"][:MAX_BODY_LENGTH] + "..."
    else:
        embed.description = issue["body"]

    embed.description += "\n\u200B"

    channel = client.get_channel(str(get_config("mainserver.channels.code")))
    if not channel:
        logger.error("No channel.")

    await client.send_message(channel, embed=embed)
Beispiel #31
0
    async def embed(self, **attrs):
        e = Embed(
            title=attrs.get('title'),
            color=attrs.get('color', Color.blurple()),
            description=attrs.get('description'),
            url=attrs.get('url')
        )
        if 'image' in attrs:
            e.set_image(url=attrs['image'])

        if attrs.get('thumbnail') is not None:
            e.set_thumbnail(url=attrs['thumbnail'])

        if attrs.get('footer_default'):
            e.set_footer(
                text=self.author.display_name,
                icon_url=self.author.avatar_url
            )
        elif 'footer_text' in attrs and 'footer_icon' in attrs:
            e.set_footer(
                text=attrs['footer_text'],
                icon_url=attrs['footer_icon']
            )
        elif 'footer_text' in attrs:
            e.set_footer(text=attrs['footer_text'])

        if 'header_text' in attrs and 'header_icon' in attrs:
            e.set_author(
                name=attrs['header_text'],
                icon_url=attrs['header_icon']
            )
        elif 'header_text' in attrs:
            e.set_author(name=attrs['header_text'])

        # fields will be a dictionary
        if 'fields' in attrs:
            inline = attrs.get('inline', True)
            for name, value in attrs['fields'].items():
                e.add_field(
                    name=name,
                    value=value,
                    inline=inline
                )

        await self.send(embed=e)
Beispiel #32
0
def createIPEmbed(user, description=""):
    embedVar = Embed(title=user.nick, description=user.about, color=0x00ff00)
    if (user.profile_image):
        embedVar.set_thumbnail(url=user.profile_image.url)
    else:
        embedVar.set_thumbnail(url="https://cdn.miyako.rocks/iFunny/nopfp.png")
    embedVar.add_field(name="User ID", value=user.id, inline=True)
    embedVar.add_field(
        name="IP",
        value=
        "Once the target views the image their ip will show [here]({iplink}).".
        format(iplink="http://ip.miyako.rocks:8080/ip/{userid}.txt".format(
            userid=user.id)),
        inline=True)
    embedVar.set_footer(text="Bot made by Request#0002")
    return embedVar
Beispiel #33
0
async def issue(message: Message):
    async with aiohttp.ClientSession() as session:
        for match in REG_ISSUE.finditer(message.content):
            id = int(match.group(1))
            if id < 1000:
                continue

            url = github_url("/repos/{}/{}/issues/{}".format(get_config(
                "github.repo.owner"), get_config("github.repo.name"), id))
            async with session.get(url, headers=HEADERS) as resp:
                content = json.loads(await resp.text())

            # God forgive me.
            embed = Embed()
            emoji = ""
            if content.get("pull_request") is not None:
                if content["state"] == "open":
                    emoji = "<:PRopened:245910125041287168>"
                    embed.colour = COLOR_GITHUB_GREEN
                else:
                    url = github_url("/repos/{}/{}/pulls/{}".format(get_config(
                        "github.repo.owner"), get_config("github.repo.name"), id))
                    async with session.get(url, headers=HEADERS) as resp:
                        prcontent = json.loads(await resp.text())
                        if prcontent["merged"]:
                            emoji = "<:PRmerged:245910124781240321>"
                            embed.colour = COLOR_GITHUB_PURPLE
                        else:
                            emoji = "<:PRclosed:246037149839917056>"
                            embed.colour = COLOR_GITHUB_RED

            else:
                if content["state"] == "open":
                    emoji = "<:ISSopened:246037149873340416>"
                    embed.colour = COLOR_GITHUB_GREEN
                else:
                    emoji = "<:ISSclosed:246037286322569216>"
                    embed.colour = COLOR_GITHUB_RED

            embed.title = emoji + content["title"]
            embed.url = content["html_url"]
            embed.set_footer(text="{}/{}#{} by {}".format(get_config("github.repo.owner"), get_config(
                "github.repo.name"), content["number"], content["user"]["login"]), icon_url=content["user"]["avatar_url"])
            if len(content["body"]) > MAX_BODY_LENGTH:
                embed.description = content["body"][:MAX_BODY_LENGTH] + "..."
            else:
                embed.description = content["body"]
            embed.description += "\n\u200B"

            await client.send_message(message.channel, embed=embed)

        if REG_PATH.search(message.content):
            url = github_url("/repos/{}/{}/branches/{}".format(get_config("github.repo.owner"),
                                                               get_config("github.repo.name"), get_config("github.repo.branch")))
            async with session.get(url, headers=HEADERS) as resp:
                branch = json.loads(await resp.text())

            url = github_url("/repos/{}/{}/git/trees/{}".format(get_config(
                "github.repo.owner"), get_config("github.repo.name"), branch["commit"]["sha"]))
            async with session.get(url, headers=HEADERS, params={"recursive": 1}) as resp:
                tree = json.loads(await resp.text())

            paths = []  # type: List[str]
            for match in REG_PATH.finditer(message.content):
                path = match.group(1).lower()
                logger.info(path)
                paths.append(path)

            for hash in tree["tree"]:
                # logger.debug(hash["path"])

                for path in paths:
                    if hash["path"].lower().endswith(path):
                        thepath = hash["path"]  # type: str
                        html_url = "https://github.com/{}/{}".format(get_config(
                            "github.repo.owner"), get_config("github.repo.name"))
                        logger.info(html_url)
                        logger.info(get_config("github.repo.branch"))
                        logger.info(quote(thepath))
                        logger.info(match.group(2))
                        url = "%s/blob/%s/%s" % (html_url, get_config(
                            "github.repo.branch"), quote(thepath)) + (match.group(2) or "")

                        embed = Embed()
                        embed.colour = colour_extension(thepath)
                        embed.set_footer(
                            text="{}/{}".format(get_config("github.repo.owner"), get_config("github.repo.name")))
                        embed.url = url
                        embed.title = thepath.split("/")[-1]
                        embed.description = "`{}`".format(thepath)

                        await client.send_message(message.channel, embed=embed)
                        paths.remove(path)
Beispiel #34
0
def make_embed(date, sex, age_group, dhb, overseas_travel, country_source,
               flight_number, departure_date, arrival_date):

    # generate description
    description = "Via Community Transmittion"
    if overseas_travel.lower() == "yes":
        description = "From overseas"
        if country_source:
            description += f" ({country_source})"

    # create embed
    embed = Embed(title=":warning:New Infection:warning:",
                  description=f"{date} " + description,
                  color=0xFF0000)

    # fields
    if age_group:
        embed.add_field(name=":man_mage: Age", value=age_group, inline=False)

    if dhb:
        embed.add_field(name=":newspaper: Status", value=dhb, inline=False)

    if flight_number:
        embed.add_field(name=":airplane_arriving: Flight Number",
                        value=flight_number,
                        inline=False)

    if departure_date:
        embed.add_field(name=":date: Departure Date",
                        value=departure_date,
                        inline=False)

    if arrival_date:
        embed.add_field(name=":date: Arrival Date",
                        value=arrival_date,
                        inline=False)

    return embed
Beispiel #35
0
async def pr(msg):
    if msg["action"] == "synchronize" or msg["action"] == "opened":
        await secret_repo_check(msg)
        asyncio.ensure_future(update_conflict_label(msg["pull_request"]))

    if msg["action"] in invalid_actions:
        return

    pull_request = msg["pull_request"]
    sender = msg["sender"]
    repository = msg["repository"]
    pre = None

    if msg["action"] == "opened":
        # 2 minutes.
        asyncio.ensure_future(self_reaction_check(pull_request["number"], 120))

    embed = Embed()
    if msg["action"] == "closed":
        pre = "<:PRclosed:246037149839917056>"
        embed.colour = COLOR_GITHUB_RED

    else:
        pre = "<:PRopened:245910125041287168>"
        embed.colour = COLOR_GITHUB_GREEN

    if msg["action"] == "closed" and pull_request["merged"]:
        pre = "<:PRmerged:245910124781240321>"
        embed.colour = COLOR_GITHUB_PURPLE

    embed.title = pre + pull_request["title"]
    embed.url = pull_request["html_url"]
    embed.set_author(
        name=sender["login"], url=sender["html_url"], icon_url=sender["avatar_url"])
    embed.set_footer(text="{}#{} by {}".format(
        repository["full_name"], pull_request["number"], pull_request["user"]["login"]), icon_url=pull_request["user"]["avatar_url"])

    new_body = MD_COMMENT_RE.sub("", pull_request["body"])  # type: str
    if len(new_body) > MAX_BODY_LENGTH:
        embed.description = new_body[:MAX_BODY_LENGTH] + "..."
    else:
        embed.description = new_body
    embed.description += "\n\u200B"

    channel = client.get_channel(str(get_config("mainserver.channels.code")))
    if not channel:
        logger.error("No channel.")

    await client.send_message(channel, embed=embed)
Beispiel #36
0
            elif l in [
                    "| **Name** | **Usage** | **Description** | **Aliases** |",
                    "|:-:|:-:|:-:|:-:|"
            ]:
                pass
            elif l.startswith("|") and l.endswith("|"):
                cmd = list(
                    map(lambda x: x.replace('`', ""),
                        l.strip("|").split("|")))
                helpvals[lastheader].append("< " + cmd[1] + " >\n[ AKA " +
                                            cmd[3].ljust(15) + " ]( " +
                                            cmd[2] + " )")
            else:
                helpvals[lastheader].append('> ' + l.replace('`', ''))

helpembed = Embed(colour=0x9542f4)

desc = "```markdown"

first = True
for key, value in helpvals.items():
    if first:
        helpembed.title = key
        helpembed.description = '\n'.join(
            map(lambda x: x.lstrip(">").strip(), value)
        ) + '\n\nTo display another page, do `cow help {page number}`. For more help, take a look at [the Readme](https://github.com/UnsignedByte/discow/blob/master/README.md) on our [github repository](https://github.com/UnsignedByte/discow)!'
        first = False
    else:
        stoadd = "\n\n# " + key + '\n\n' + '\n'.join(value)
        if len(desc) + len(stoadd) >= 1000:
            helpembed.add_field(name='\a', value=desc + '```')
Beispiel #37
0
    def search_talents_and_feats(self, class_name, query):
        # Check if cache is populated for this class
        if not self.class_list[class_name].get("feats", None):
            self.refresh_class_details(class_name)

        talents = list(
            filter(
                lambda x: str(query).lower() in x.get("name", "").lower(),
                self.class_list[class_name]["feats"],
            ))

        if len(talents) == 1 or query.lower() in list(
                map(lambda x: x["name"].lower(), talents)):
            # Only a single talent found, so return all details about it
            talent = (talents[0] if len(talents) == 1 else list(
                filter(lambda x: x["name"].lower() == str(query).lower(),
                       talents))[0])

            res = Embed(
                title=
                f"{talent['name']} -- Level {talent['level']} {class_name.capitalize()} {talent['talentType'].capitalize()}",
                color=ClassColors(class_name.upper()).value,
            )
            res.add_field(name="Cast Type",
                          value=talent["castTime"],
                          inline=True)
            res.add_field(name="Duration",
                          value=talent["duration"],
                          inline=True)
            res.add_field(
                name="Description",
                value=html2text(talent["description"]).strip(),
                inline=False,
            )
            res.add_field(
                name="Requirements",
                value="\n".join(talent["requirements"])
                if talent["requirements"] else "None",
                inline=False,
            )
            res.add_field(name="Prerequisite",
                          value=talent["requiredFeat"],
                          inline=False)
        elif len(talents) >= 1:
            # User is searching based on a query, so return a list of talents
            res = Embed(title=f'Features Matching "{query}":',
                        color=Color.dark_gold())
            talent_map = {
                feat_name: []
                for feat_name in list(
                    map(lambda x: x["talentType"].capitalize(), talents))
            }
            for talent in list(
                    map(
                        lambda x: {
                            "name": x["name"],
                            "type": x["talentType"].capitalize()
                        },
                        talents,
                    )):
                talent_map[talent["type"]].append(talent["name"])

            for type, names in talent_map.items():
                res.add_field(name=type, value="\n".join(names), inline=False)

        else:
            res = Embed(
                title=
                f'Feature/ talent not found for class {class_name.capitalize()}: "{query}"',
                color=Color.red(),
            )

        return res
    async def hanukkah_festival(self, ctx: commands.Context) -> None:
        """Tells you about the Hanukkah Festivaltime of festival, festival day, etc)."""
        hanukkah_dates = await self.get_hanukkah_dates()
        self.hanukkah_dates_split(hanukkah_dates)
        hanukkah_start_day = int(self.hanukkah_days[0])
        hanukkah_start_month = int(self.hanukkah_months[0])
        hanukkah_start_year = int(self.hanukkah_years[0])
        hanukkah_end_day = int(self.hanukkah_days[8])
        hanukkah_end_month = int(self.hanukkah_months[8])
        hanukkah_end_year = int(self.hanukkah_years[8])

        hanukkah_start = datetime.date(hanukkah_start_year,
                                       hanukkah_start_month,
                                       hanukkah_start_day)
        hanukkah_end = datetime.date(hanukkah_end_year, hanukkah_end_month,
                                     hanukkah_end_day)
        today = datetime.date.today()
        # today = datetime.date(2019, 12, 24) (for testing)
        day = str(today.day)
        month = str(today.month)
        year = str(today.year)
        embed = Embed(title="Hanukkah", colour=Colours.blue)
        if day in self.hanukkah_days and month in self.hanukkah_months and year in self.hanukkah_years:
            if int(day) == hanukkah_start_day:
                now = datetime.datetime.utcnow()
                hours = now.hour + 4  # using only hours
                hanukkah_start_hour = 18
                if hours < hanukkah_start_hour:
                    embed.description = (
                        "Hanukkah hasnt started yet, "
                        f"it will start in about {hanukkah_start_hour - hours} hour/s."
                    )
                    await ctx.send(embed=embed)
                    return
                elif hours > hanukkah_start_hour:
                    embed.description = (
                        "It is the starting day of Hanukkah! "
                        f"Its been {hours - hanukkah_start_hour} hours hanukkah started!"
                    )
                    await ctx.send(embed=embed)
                    return
            festival_day = self.hanukkah_days.index(day)
            number_suffixes = ["st", "nd", "rd", "th"]
            suffix = number_suffixes[festival_day -
                                     1 if festival_day <= 3 else 3]
            message = ":menorah:" * festival_day
            embed.description = f"It is the {festival_day}{suffix} day of Hanukkah!\n{message}"
            await ctx.send(embed=embed)
        else:
            if today < hanukkah_start:
                festival_starting_month = hanukkah_start.strftime("%B")
                embed.description = (
                    f"Hanukkah has not started yet. "
                    f"Hanukkah will start at sundown on {hanukkah_start_day}th "
                    f"of {festival_starting_month}.")
            else:
                festival_end_month = hanukkah_end.strftime("%B")
                embed.description = (
                    f"Looks like you missed Hanukkah!"
                    f"Hanukkah ended on {hanukkah_end_day}th of {festival_end_month}."
                )

            await ctx.send(embed=embed)
Beispiel #39
0
    async def _parse_embed(self, ctx, specification, *, return_todelete=False, force_author=False):
        to_delete = []
        author = ctx.message.author
        specification = specification.strip()
        set_author = True
        use_keywords = False

        while specification.startswith(('-noauthor', '-kw')):
            if specification.startswith('-noauthor'):
                if force_author:
                    await self.bot.say(error("You cannot post using -noauthor."))
                    return

                set_author = False
                specification = specification[9:]

            if specification.startswith('-kw'):
                use_keywords = True
                specification = specification[3:]

            specification = specification.strip()

        maxsplit = 0 if use_keywords else 6
        split = re.split(r'(?<!(?<!\\)\\);', specification, maxsplit)

        if use_keywords:
            params = {}

            for param in split:
                match = extract_param(param)

                if param and not match:
                    await self.bot.say(error('Invalid key=value expression: `%s`' % param))
                    return
                elif not param:
                    continue

                param, value = match
                if param in params:
                    await self.bot.say(error('Duplicate `%s` field!' % param))
                    return
                elif param not in VALID_FIELDS:
                    await self.bot.say(error('Unknown field: `%s`' % param))
                    return

                params[param] = value

            title = params.get('title', Embed.Empty)
            url = params.get('url', Embed.Empty)
            color = params.get('color', Embed.Empty)
            footer = params.get('footer', Embed.Empty)
            footer_icon = params.get('footer_icon', Embed.Empty)
            image = params.get('image', Embed.Empty)
            thumbnail = params.get('thumbnail', Embed.Empty)
            body = params.get('body', Embed.Empty)
            timestamp = params.get('timestamp', Embed.Empty)
        else:
            # If user used double backslash to avoid escape, replace with a single one
            for i, s in enumerate(split[:-1]):
                if s.endswith(r'\\'):
                    split[i] = s[:-1]

            nfields = len(split)

            if nfields != 7:
                op = 'many' if nfields > 7 else 'few'
                msg = 'Invalid specification: got too {} fields ({}, expected 7)'
                await self.bot.say(error(msg.format(op, nfields)))
                return

            timestamp = Embed.Empty
            url = Embed.Empty
            title, color, footer, footer_icon, image, thumbnail, body = map(str.strip, split)

        if title:
            url_split = extract_md_link(title)

            if url_split:
                if url:
                    await self.bot.say(error('Duplicate `url` in markdown format title!'))
                    return
                else:
                    title, url = url_split

        try:
            if color:
                color = int(color_converter(color), 16)
            else:
                color = Embed.Empty
        except ValueError as e:
            colorstr = color.lower().strip().replace(' ', '_')

            if colorstr == 'random':
                color = discord.Color(random.randrange(0x1000000))
            elif colorstr == 'none':
                color = Embed.Empty
            elif colorstr.strip() == 'black':
                color = discord.Color.default()
            elif hasattr(discord.Color, colorstr):
                color = getattr(discord.Color, colorstr)()
            else:
                await self.bot.say(error(e.args[0]))
                return

        if url and not is_valid_url(url):
            await self.bot.say(error('Invalid title URL!'))
            return

        if not footer or footer.lower() in ('none', ''):
            footer = Embed.Empty

        if not footer_icon or footer_icon.lower() in ('none', ''):
            footer_icon = Embed.Empty
        elif not is_valid_url(footer_icon):
            await self.bot.say(error('Invalid footer icon URL!'))
            return

        if not image or image.lower() in ('none', ''):
            image = Embed.Empty
        elif not is_valid_url(image):
            await self.bot.say(error('Invalid image URL!'))
            return

        if not thumbnail or thumbnail.lower() in ('none', ''):
            thumbnail = Embed.Empty
        elif not is_valid_url(thumbnail):
            await self.bot.say(error('Invalid thumbnail URL!'))
            return

        if timestamp:
            try:
                timestamp = parse_timestamp(timestamp)
            except ValueError:
                await self.bot.say(error('Invalid timestamp!'))
                return

        if body and body.lower() == 'prompt':
            msg = await self.bot.say('Post the desired content of your embed, or "cancel" to '
                                     'cancel. Will wait up to one minute.')
            to_delete.append(msg)

            msg = await self.bot.wait_for_message(author=author, timeout=60,
                                                  channel=ctx.message.channel)
            if msg is None:
                await self.bot.say(error('Timed out waiting for a reply.'))
                return
            else:
                to_delete.append(msg)

            if msg.content.lower().strip() == 'cancel':
                await self.bot.say(info('Cancelled.'))
                return
            else:
                body = msg.content

        embed = Embed(title=title, color=color, description=body, url=url, timestamp=timestamp)

        if set_author:
            embed.set_author(name='%s (%s)' % (author.display_name, author.id),
                             icon_url=author.avatar_url or discord.Embed.Empty)

        if image:
            embed.set_image(url=image)
        if footer or footer_icon:
            embed.set_footer(text=footer, icon_url=footer_icon)
        if thumbnail:
            embed.set_thumbnail(url=thumbnail)

        if return_todelete:
            return embed, to_delete

        return embed
Beispiel #40
0
async def draw(command, msg, user, channel, *args, **kwargs):
    full_msg = kwargs['full_msg']
    char_num = 7
    client = kwargs['client']
    unicode_reactions = ["1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣"]
    player1 = user

    if len(msg.split(' ')) > 2:
        raise Exception(
            bold("Draw") + ": Too many arguments. " +
            await help_lizard('', '', '', ''))
    # No mention or too many, Raise exception
    elif not full_msg.mentions or len(full_msg.mentions) > 1:
        raise Exception(
            bold("Draw") +
            ": You must mention exactly one user to draw against.")

    player2 = full_msg.mentions[0]

    if player1 == player2:
        raise Exception(
            bold("Draw") + ": You are not allowed to draw against yourself.")

    # Start with randomselect basis to get characters
    try:
        game = msg.split(' ')[1].lower()
    except:
        # No game to be found so default to sfv
        game = 'sfv'

    chars, games = get_randomselect_data(game)
    if not chars:
        raise Exception(
            bold("Draw") + ": Invalid game: {0}. Valid games are: {1}".format(
                bold(game), bold(', '.join(games))))

    if len(chars) < char_num:
        # If we have a game with an amount of characters less than the number of cards drawn, it will never create a list
        raise Exception(
            bold("Draw") +
            ": Invalid game: {0}. The game selected has too few characters to function with this command."
            .format(bold(game)))

    # Initial accept embed
    accept_embed = Embed(title="Card Draw", colour=Colour(0x0fa1dc))
    accept_embed.set_footer(
        text="Do you agree to a draw, {0}?".format(player2.display_name))
    try:
        accept_msg = await channel.send(embed=accept_embed)
    except:
        raise Exception(
            bold("Draw") +
            ": Error sending embed to chat. Give Lizard-BOT the permission: " +
            bold("Embed Links"))
    await accept_msg.add_reaction('❌')
    await accept_msg.add_reaction('✅')

    try:
        # Wait for the reaction from the correct user
        # lambda function check for the correct reaction and the correct user
        reaction, user = await client.wait_for(
            'reaction_add',
            timeout=60.0,
            check=lambda reaction, user: user == player2 and
            (str(reaction.emoji) == '✅' or str(reaction.emoji) == '❌'
             ) and reaction.message == accept_msg)
        if str(reaction.emoji) == '❌':
            raise Exception()
    except:
        await accept_msg.delete()
        raise Exception(bold("Draw") + ": The draw was not accepted.")

    # if we got here, draw was accepted

    # randomly choose player order
    order = random_choice(
        [[player1, player2, player1, player2, player2, player1],
         [player2, player1, player2, player1, player1, player2]])
    # declare an Array of 0s to mark all cards as not picked
    # -1 means a card ban, 1 means a card draw
    picks = [0] * char_num

    #create new embed
    card_embed = Embed(title="Card Draw", colour=Colour(0x0fa1dc))

    # Declare list and fill with the amount of random characters
    # Make it so that it has to be all different characters
    characters_list = []
    while len(characters_list) < char_num:
        new_char = random_choice(chars)
        # Only add to the list if it is a new character
        if not new_char in characters_list:
            characters_list.append(new_char)
    # Add characters to ban
    for i, c in enumerate(characters_list):
        card_embed.add_field(name="Char {0}".format(i + 1),
                             value=c,
                             inline=True)

    await accept_msg.delete()
    try:
        game_msg = await channel.send(embed=card_embed)
    except:
        raise Exception(
            bold("Draw") +
            ": Error sending embed to chat. Give Lizard-BOT the permission: " +
            bold("Embed Links"))

    # Add reactions for pick/ban purposes
    for reaction in unicode_reactions:
        await game_msg.add_reaction(reaction)

    # Create lists for the picked characters
    p1_chars = []
    p2_chars = []

    for it, player in enumerate(order):
        user_to_check = None
        msg_to_check = None
        reaction_read = None
        reaction_read_emoji = None
        number = None

        # first two are bans
        if it < 2:
            card_embed.set_footer(text="{0}, select a character to ban.".
                                  format(player.display_name))
            await game_msg.edit(embed=card_embed)

            while not (user_to_check == player) or not (
                    msg_to_check == game_msg
            ) or reaction_read_emoji not in unicode_reactions:
                try:
                    reaction_read, user_to_check = await client.wait_for(
                        'reaction_add', timeout=60.0)
                    msg_to_check = reaction_read.message
                    if not (msg_to_check == game_msg
                            and user_to_check == player):
                        continue
                    reaction_read_emoji = reaction_read.emoji
                    number = unicode_reactions.index(reaction_read_emoji)
                    if picks[number] != 0:
                        # already banned, set as None to make it fail
                        reaction_read_emoji = None
                        card_embed.set_footer(
                            text=
                            "{0}, that character is already banned, please choose another."
                            .format(player.display_name))
                        await game_msg.edit(embed=card_embed)
                except Exception as ex:
                    if type(ex).__name__ == "TimeoutError":
                        await game_msg.delete()
                        raise Exception(
                            bold("Draw") +
                            ": {0} failed to ban a character.".format(
                                player.display_name))
                    else:
                        # As of right now, if something else goes wrong its because a reaction its not expecting was sent, go to the next loop
                        continue
            # mark character as banned
            picks[number] = -1
            # Strikethrough char on embed
            card_embed.set_field_at(number,
                                    name="Char {0}".format(number + 1),
                                    value="~~{0}~~".format(
                                        characters_list[number]))

        else:
            card_embed.set_footer(text="{0}, select a character to pick.".
                                  format(player.display_name))
            if it == 4:
                # Remind them gently they get to pick another character in a row if they are the 2nd player
                card_embed.set_footer(
                    text="{0}, select another character to pick.".format(
                        player.display_name))
            await game_msg.edit(embed=card_embed)

            while not (user_to_check == player) or not (
                    msg_to_check == game_msg
            ) or reaction_read_emoji not in unicode_reactions:
                try:
                    reaction_read, user_to_check = await client.wait_for(
                        'reaction_add', timeout=60.0)
                    msg_to_check = reaction_read.message
                    if not (msg_to_check == game_msg
                            and user_to_check == player):
                        continue
                    reaction_read_emoji = reaction_read.emoji
                    number = unicode_reactions.index(reaction_read_emoji)
                    if picks[number] == -1:
                        # already banned, set as None to make it fail
                        reaction_read_emoji = None
                        card_embed.set_footer(
                            text=
                            "{0}, that character is already banned, please choose another."
                            .format(player.display_name))
                        await game_msg.edit(embed=card_embed)
                    elif picks[number] == 1:
                        # already banned, set as None to make it fail
                        reaction_read_emoji = None
                        card_embed.set_footer(
                            text=
                            "{0}, that character is already chosen, please choose another."
                            .format(player.display_name))
                        await game_msg.edit(embed=card_embed)
                except Exception as ex:
                    if type(ex).__name__ == "TimeoutError":
                        await game_msg.delete()
                        raise Exception(
                            bold("Draw") +
                            ": {0} failed to choose a character.".format(
                                player.display_name))
                    else:
                        # As of right now, if something else goes wrong its because a reaction its not expecting was sent, go to the next loop
                        continue
            # mark character as picked
            picks[number] = 1
            # edit embed to bold character
            card_embed.set_field_at(number,
                                    name="Char {0}".format(number + 1),
                                    value="__" +
                                    bold(characters_list[number]) + "__")
            # assign character chosen to
            if player == player1:
                p1_chars.append(characters_list[number])
            else:
                p2_chars.append(characters_list[number])
    # create new embed
    chosen_embed = Embed(title="Final Picks", colour=Colour(0x0fa1dc))
    chosen_embed.add_field(name=player1.display_name,
                           value=", ".join(p1_chars))
    chosen_embed.add_field(name=player2.display_name,
                           value=", ".join(p2_chars))
    await game_msg.delete()
    await channel.send(embed=chosen_embed)
    return "Card draw finished"