Exemple #1
0
    async def night_core(self, ctx: custom.Context) -> None:
        """
        Sets a nightcore audio filter on the player.
        """

        if enums.Filters.NIGHTCORE in ctx.voice_client.enabled_filters:
            await ctx.voice_client.set_filter(
                slate.obsidian.Filter(ctx.voice_client.filter,
                                      timescale=slate.obsidian.Timescale()))
            ctx.voice_client.enabled_filters.remove(enums.Filters.NIGHTCORE)
            embed = utils.embed(
                colour=colours.GREEN,
                description="**Nightcore** audio effect is now **inactive**.")

        else:
            await ctx.voice_client.set_filter(
                slate.obsidian.Filter(ctx.voice_client.filter,
                                      timescale=slate.obsidian.Timescale(
                                          speed=1.12, pitch=1.12)))
            ctx.voice_client.enabled_filters.add(enums.Filters.NIGHTCORE)
            embed = utils.embed(
                colour=colours.GREEN,
                description="**Nightcore** audio effect is now **active**.")

        await ctx.reply(embed=embed)
Exemple #2
0
    async def _8d(self, ctx: custom.Context) -> None:
        """
        Sets an 8D audio filter on the player.
        """

        if enums.Filters.ROTATION in ctx.voice_client.enabled_filters:
            await ctx.voice_client.set_filter(
                slate.obsidian.Filter(ctx.voice_client.filter,
                                      rotation=slate.obsidian.Rotation()))
            ctx.voice_client.enabled_filters.remove(enums.Filters.ROTATION)
            embed = utils.embed(
                colour=colours.GREEN,
                description="**8D** audio effect is now **inactive**.")

        else:
            await ctx.voice_client.set_filter(
                slate.obsidian.Filter(
                    ctx.voice_client.filter,
                    rotation=slate.obsidian.Rotation(rotation_hertz=0.5)))
            ctx.voice_client.enabled_filters.add(enums.Filters.ROTATION)
            embed = utils.embed(
                colour=colours.GREEN,
                description="**8D** audio effect is now **active**.")

        await ctx.reply(embed=embed)
Exemple #3
0
    async def save(self, ctx: custom.Context) -> None:
        """
        Saves the current track to our DM's.
        """

        try:

            embed = utils.embed(
                title=f"{ctx.voice_client.current.title}",
                url=f"{ctx.voice_client.current.uri}",
                image=ctx.voice_client.current.thumbnail,
                description=f"**Author:** {ctx.voice_client.current.author}\n"
                f"**Source:** {ctx.voice_client.current.source.name.title()}\n"
                f"**Length:** {utils.format_seconds(ctx.voice_client.current.length // 1000, friendly=True)}\n"
                f"**Is stream:** {ctx.voice_client.current.is_stream()}\n"
                f"**Is seekable:** {ctx.voice_client.current.is_seekable()}\n"
                f"**Requester:** {ctx.voice_client.current.requester} `{ctx.voice_client.current.requester.id}`"
            )
            await ctx.author.send(embed=embed)
            await ctx.reply(embed=utils.embed(
                colour=colours.GREEN,
                description="Saved the current track to our DM's."))

        except discord.Forbidden:
            raise exceptions.EmbedError(colour=colours.RED,
                                        description="I am unable to DM you.")
Exemple #4
0
    async def todo_add(self, ctx: custom.Context, *,
                       content: converters.TodoContentConverter) -> None:
        """
        Creates a todo.

        **content**: The content of your todo. Must be under 150 characters.

        **Usage:**
        `l-todo Finish documentation`
        """

        user_config = await self.bot.user_manager.get_config(ctx.author.id)

        if len(user_config.todos) > 100:
            raise exceptions.EmbedError(
                colour=colours.RED,
                description=
                "You have 100 todos, try finishing some before adding any more.",
            )

        todo = await user_config.create_todo(content=str(content),
                                             jump_url=ctx.message.jump_url)

        await ctx.reply(embed=utils.embed(
            colour=colours.GREEN, description=f"Todo **{todo.id}** created."))
Exemple #5
0
    async def source(self, ctx: custom.Context, *, command: Optional[str]) -> None:

        if not command:
            await ctx.reply(
                embed=utils.embed(
                    emoji="\U0001f4da",
                    description=f"My source code can be viewed here: **{values.GITHUB_LINK}**"
                )
            )
            return

        if command == "help":
            source = type(self.bot.help_command)
            module = source.__module__
            filename: str = str(inspect.getsourcefile(source))

        else:
            if (obj := self.bot.get_command(command.replace(".", ""))) is None:
                raise exceptions.EmbedError(
                    colour=colours.RED,
                    emoji=emojis.CROSS,
                    description="I couldn't find that command."
                )

            source = obj.callback.__code__
            module = obj.callback.__module__
            filename = source.co_filename
Exemple #6
0
    async def sort(self,
                   ctx: custom.Context,
                   method: Literal["title", "length", "author"],
                   reverse: bool = False) -> None:
        """
        Sorts the queue.

        **method**: The method to sort the queue with. Can be **title**, **length** or **author**.
        **reverse**: Whether to reverse the sort, as in **5, 3, 2, 4, 1** -> **5, 4, 3, 2, 1** instead of **5, 3, 2, 4, 1** -> **1, 2, 3, 4, 5**. Defaults to False.

        **Usage:**
        `l-sort title True`
        `l-sort author`
        `l-sort length True`
        """

        if method == "title":
            ctx.voice_client.queue._queue.sort(key=lambda track: track.title,
                                               reverse=reverse)
        elif method == "author":
            ctx.voice_client.queue._queue.sort(key=lambda track: track.author,
                                               reverse=reverse)
        elif method == "length":
            ctx.voice_client.queue._queue.sort(key=lambda track: track.length,
                                               reverse=reverse)

        await ctx.reply(embed=utils.embed(
            colour=colours.GREEN,
            description=f"The queue has been sorted by**{method}**."))
Exemple #7
0
    async def tag_transfer(self, ctx: custom.Context, tag: objects.Tag, *,
                           member: discord.Member) -> None:
        """
        Transfers a tag to another member.

        **name**: The name of the tag to transfer.
        **member**: The member to transfer the tag too. Can be their ID, Username, Nickname or @Mention.
        """

        if member.bot:
            raise exceptions.EmbedError(
                colour=colours.RED,
                description="You can not transfer tags to bots.")
        if tag.user_id != ctx.author.id:
            raise exceptions.EmbedError(colour=colours.RED,
                                        description="You do not own that tag.")
        if tag.user_id == member.id:
            raise exceptions.EmbedError(
                colour=colours.RED,
                description="You can not transfer tags to yourself.")

        await tag.change_owner(user_id=member.id)

        await ctx.reply(embed=utils.embed(
            colour=colours.GREEN,
            emoji=emojis.TICK,
            description=
            f"Transferred tag **{tag.name}** from **{ctx.author}** to **{ctx.guild.get_member(tag.user_id)}**.",
        ))
Exemple #8
0
    def __init__(
        self,
        footer_url: str | None = None,
        footer: str | None = None,
        image: str | None = None,
        thumbnail: str | None = None,
        author: str | None = None,
        author_url: str | None = None,
        author_icon_url: str | None = None,
        title: str | None = None,
        description: str | None = None,
        url: str | None = None,
        colour: discord.Colour = colours.MAIN,
        emoji: str | None = None,
        view: discord.ui.View | None = None,
    ) -> None:

        self.embed: discord.Embed = utils.embed(
            footer_url=footer_url,
            footer=footer,
            image=image,
            thumbnail=thumbnail,
            author=author,
            author_url=author_url,
            author_icon_url=author_icon_url,
            title=title,
            description=description,
            url=url,
            colour=colour,
            emoji=emoji,
        )
        self.view: discord.ui.View | None = view
Exemple #9
0
    async def dev_cleanup(self, ctx: custom.Context, limit: int = 100) -> None:
        """
        Deletes the bots messages.

        **limit**: The amount of messages to check back through to delete.
        """

        if ctx.channel.permissions_for(ctx.me).manage_messages:
            messages = await ctx.channel.purge(
                check=lambda message: message.author == ctx.me or message.
                content.startswith(config.PREFIX),
                limit=limit)
        else:
            messages = await ctx.channel.purge(
                check=lambda message: message.author == ctx.me,
                bulk=False,
                limit=limit)

        s = "s" if len(messages) > 1 else ""
        s1 = "s" if limit > 1 else ""  # TODO: Pluraliser or something??

        embed = utils.embed(
            colour=colours.GREEN,
            emoji=emojis.TICK,
            description=
            f"Found and deleted **{len(messages)}** message{s} out of the last **{limit}** message{s1}.",
        )
        await ctx.reply(embed=embed, delete_after=10)
Exemple #10
0
    async def _birthday_set(self, ctx: custom.Context, *, date: objects.PastPhrasedDatetimeSearch) -> None:
        """
        Sets your birthday.

        **date**: Your birthday. This should include some form of date such as **tomorrow**, **in 3 weeks** or **1st january 2020**.
        """

        entries = {index: (phrase, datetime) for index, (phrase, datetime) in enumerate(date.datetimes.items())}

        choice = await ctx.choice(
            entries=[f"`{index + 1}:` {phrase}\n{utils.format_date(datetime)}" for index, (phrase, datetime) in entries.items()],
            per_page=5,
            splitter="\n\n",
            title="Type the number of the date you want to set your birthday too:",
        )
        _, birthday = entries[choice]

        if birthday > pendulum.now(tz="UTC").subtract(years=13):
            raise exceptions.EmbedError(
                colour=colours.RED,
                emoji=emojis.CROSS,
                description="Your birthday must allow you to be more than 13 years old.",
            )

        user_config = await self.bot.user_manager.get_config(ctx.author.id)
        await user_config.set_birthday(birthday)

        embed = utils.embed(
            colour=colours.GREEN,
            emoji=emojis.TICK,
            description=f"Birthday set to **{utils.format_date(user_config.birthday)}**",
        )
        await ctx.reply(embed=embed)
Exemple #11
0
    async def join(self, ctx: custom.Context) -> None:
        """
        Joins the bot to your voice channel.
        """

        if ctx.voice_client and ctx.voice_client.is_connected():
            raise exceptions.EmbedError(
                colour=colours.RED,
                emoji=emojis.CROSS,
                description=
                f"I am already connected to {ctx.voice_client.voice_channel.mention}.",
            )

        if not ctx.author.voice or not ctx.author.voice.channel:
            raise exceptions.EmbedError(
                colour=colours.RED,
                description=
                "You must be connected to a voice channel to use this command.",
            )

        # noinspection PyTypeChecker
        await ctx.author.voice.channel.connect(cls=custom.Player)
        ctx.voice_client._text_channel = ctx.channel

        await ctx.send(embed=utils.embed(
            colour=colours.GREEN,
            emoji=emojis.TICK,
            description=f"Joined {ctx.voice_client.voice_channel.mention}."))
Exemple #12
0
    async def _reminders_delete(self, ctx: custom.Context, reminders: commands.Greedy[objects.Reminder]) -> None:
        """
        Deletes reminders with the given id's.

        **reminders**: A list of reminders id's to delete, separated by spaces.
        """

        if not reminders:
            raise exceptions.EmbedError(
                colour=colours.RED,
                emoji=emojis.CROSS,
                description="One or more of the reminder id's provided were invalid."
            )

        for reminder in reminders:
            await reminder.delete()

        s = "s" if len(reminders) > 1 else ""

        embed = utils.embed(
            colour=colours.GREEN,
            emoji=emojis.TICK,
            description=f"Deleted **{len(reminders)}** reminder{s} with id{s} {', '.join(f'**{reminder.id}**' for reminder in reminders)}.",
        )
        await ctx.reply(embed=embed)
Exemple #13
0
    async def move(self,
                   ctx: custom.Context,
                   entry_1: int = 0,
                   entry_2: int = 0) -> None:
        """
        Move a track in the queue to a different position.

        **entry_1**: The position of the track you want to move from.
        **entry_2**: The position of the track you want to move too.
        """

        if entry_1 <= 0 or entry_1 > len(ctx.voice_client.queue):
            raise exceptions.EmbedError(
                colour=colours.RED,
                description=
                f"That was not a valid track entry to move from, try again with a number between **1** and **{len(ctx.voice_client.queue)}**.",
            )

        if entry_2 <= 0 or entry_2 > len(ctx.voice_client.queue):
            raise exceptions.EmbedError(
                colour=colours.RED,
                description=
                f"That was not a valid track entry to move too, try again with a number between **1** and **{len(ctx.voice_client.queue)}**.",
            )

        track = ctx.voice_client.queue.get(entry_1 - 1, put_history=False)
        ctx.voice_client.queue.put(track, position=entry_2 - 1)

        embed = utils.embed(
            colour=colours.GREEN,
            description=
            f"Moved **[{track.title}]({track.uri})** from position **{entry_1}** to position **{entry_2}**.",
        )
        await ctx.reply(embed=embed)
Exemple #14
0
    async def reverse(self, ctx: custom.Context) -> None:
        """
        Reverses the queue.
        """

        ctx.voice_client.queue.reverse()
        await ctx.reply(embed=utils.embed(
            colour=colours.GREEN, description="The queue has been reversed."))
Exemple #15
0
    async def clear(self, ctx: custom.Context) -> None:
        """
        Clears the queue.
        """

        ctx.voice_client.queue.clear()
        await ctx.reply(embed=utils.embed(
            colour=colours.GREEN, description="The queue has been cleared."))
Exemple #16
0
    async def handle_track_error(self) -> None:

        await self.send(embed=utils.embed(
            colour=colours.RED,
            description="Something went wrong while playing a track.",
        ),
                        view=views.SupportButton())

        await self.handle_track_over()
Exemple #17
0
    async def invoke_controller(
            self,
            channel: discord.TextChannel | None = None
    ) -> discord.Message | None:

        if (channel is None
                and self.text_channel is None) or self.current is None:
            return

        text_channel = channel or self.text_channel
        if text_channel is None:
            return

        guild_config = await self.client.guild_manager.get_config(
            text_channel.guild.id)

        embed = utils.embed(
            title="Now playing:",
            description=
            f"**[{self.current.title}]({self.current.uri})**\nBy **{self.current.author}**",
            thumbnail=self.current.thumbnail)

        if guild_config.embed_size.value >= enums.EmbedSize.MEDIUM.value:

            embed.add_field(
                name="__Player info:__",
                value=f"**Paused:** {self.paused}\n"
                f"**Loop mode:** {self.queue.loop_mode.name.title()}\n"
                f"**Queue length:** {len(self.queue)}\n"
                f"**Queue time:** {utils.format_seconds(sum(track.length for track in self.queue) // 1000, friendly=True)}\n",
            )
            embed.add_field(
                name="__Track info:__",
                value=
                f"**Time:** {utils.format_seconds(self.position // 1000)} / {utils.format_seconds(self.current.length // 1000)}\n"
                f"**Is Stream:** {self.current.is_stream()}\n"
                f"**Source:** {self.current.source.value.title()}\n"
                f"**Requester:** {self.current.requester.mention if self.current.requester else 'N/A'}\n"
            )

        if guild_config.embed_size is enums.EmbedSize.LARGE and not self.queue.is_empty(
        ):

            entries = [
                f"**{index + 1}.** [{entry.title}]({entry.uri})"
                for index, entry in enumerate(list(self.queue)[:3])
            ]
            if len(self.queue) > 3:
                entries.append(
                    f"**...**\n**{len(self.queue)}.** [{self.queue[-1].title}]({self.queue[-1].uri})"
                )

            embed.add_field(name="__Up next:__",
                            value="\n".join(entries),
                            inline=False)

        return await text_channel.send(embed=embed)
Exemple #18
0
    async def disconnect(self, ctx: custom.Context) -> None:
        """
        Disconnects the bot from its voice channel.
        """

        await ctx.send(embed=utils.embed(
            colour=colours.GREEN,
            emoji=emojis.TICK,
            description=f"Left {ctx.voice_client.voice_channel.mention}."))
        await ctx.voice_client.disconnect()
Exemple #19
0
    async def notifications(self, ctx: custom.Context) -> None:
        """
        Shows your current notification settings.
        """

        user_config = await self.bot.user_manager.get_config(ctx.author.id)

        await ctx.send(embed=utils.embed(
            title=f"Notification settings for **{ctx.author}**:",
            description=
            f"**Levels up:** {utils.readable_bool(user_config.notifications.level_ups)}"
        ))
Exemple #20
0
    async def resume(self, ctx: custom.Context) -> None:
        """
        Resumes the current track.
        """

        if ctx.voice_client.paused is False:
            raise exceptions.EmbedError(
                colour=colours.RED, description="The player is not paused.")

        await ctx.voice_client.set_pause(False)
        await ctx.reply(embed=utils.embed(
            colour=colours.GREEN, description="The player is now **resumed**.")
                        )
Exemple #21
0
    async def pause(self, ctx: custom.Context) -> None:
        """
        Pauses the current track.
        """

        if ctx.voice_client.paused is True:
            raise exceptions.EmbedError(
                colour=colours.RED,
                description="The played is already paused.")

        await ctx.voice_client.set_pause(True)
        await ctx.reply(embed=utils.embed(
            colour=colours.GREEN, description="The player is now **paused**."))
Exemple #22
0
    async def replay(self, ctx: custom.Context) -> None:
        """
        Replays the current track.
        """

        await ctx.voice_client.set_position(0)

        embed = utils.embed(
            colour=colours.GREEN,
            description=
            f"Replaying [{ctx.voice_client.current.title}]({ctx.voice_client.current.uri}) by **{ctx.voice_client.current.author}**."
        )
        await ctx.reply(embed=embed)
Exemple #23
0
async def edit_image(ctx: custom.Context, edit_function: Callable[..., Any],
                     image: objects.Image, **kwargs) -> None:

    embed = utils.embed(colour=colours.GREEN,
                        emoji=emojis.LOADING,
                        description="Processing image")
    message = await ctx.reply(embed=embed)

    image_bytes = await request_image_bytes(session=ctx.bot.session,
                                            url=image.url)
    receiving_pipe, sending_pipe = multiprocessing.Pipe(duplex=False)

    process = multiprocessing.Process(target=do_edit_image,
                                      daemon=True,
                                      args=(edit_function, image_bytes,
                                            sending_pipe),
                                      kwargs=kwargs)
    process.start()

    data = await ctx.bot.loop.run_in_executor(None, receiving_pipe.recv)

    process.join()

    receiving_pipe.close()
    sending_pipe.close()
    process.terminate()
    process.close()

    if data is ValueError or data is EOFError:

        try:
            await message.delete()
        except Exception:
            pass

        raise exceptions.EmbedError(
            colour=colours.RED,
            description="Something went wrong while editing that image.")

    url = await utils.upload_file(ctx.bot.session,
                                  file_bytes=data[0],
                                  file_format=data[1])

    try:
        await message.delete()
    except Exception:
        pass

    await ctx.reply(url)

    del data
Exemple #24
0
    async def fast_forward(self, ctx: custom.Context, *,
                           time: objects.Time) -> None:
        """
        Seeks the player forward.

        **time**: The amount of time to seek forward.

        Valid time formats include:

        - 01:10:20 (hh:mm:ss)
        - 01:20 (mm:ss)
        - 20 (ss)
        - (h:m:s)
        - (hh:m:s)
        - (h:mm:s)
        - etc

        - 1 hour 20 minutes 30 seconds
        - 1 hour 20 minutes
        - 1 hour 30 seconds
        - 20 minutes 30 seconds
        - 20 minutes
        - 30 seconds
        - 1 hour 20 minutes and 30 seconds
        - 1h20m30s
        - 20m and 30s
        - 20s
        - etc
        """

        milliseconds = time.seconds * 1000
        position = ctx.voice_client.position
        remaining = ctx.voice_client.current.length - position

        if milliseconds >= remaining:
            raise exceptions.EmbedError(
                colour=colours.RED,
                description=
                f"That was too much time to seek forward, try seeking forward an amount less than "
                f"**{utils.format_seconds(remaining // 1000, friendly=True)}**.",
            )

        await ctx.voice_client.set_position(position + milliseconds)

        embed = utils.embed(
            colour=colours.GREEN,
            description=
            f"Seeking forward **{utils.format_seconds(time.seconds, friendly=True)}**, the players position is now "
            f"**{utils.format_seconds(ctx.voice_client.position // 1000, friendly=True)}**."
        )
        await ctx.reply(embed=embed)
Exemple #25
0
    async def _timezone_reset(self, ctx: custom.Context) -> None:
        """
        Resets your timezone.
        """

        user_config = await self.bot.user_manager.get_config(ctx.author.id)
        await user_config.set_timezone()

        embed = utils.embed(
            colour=colours.GREEN,
            emoji=emojis.TICK,
            description=f"Your timezone has been reset back to **{None}**.",
        )
        await ctx.reply(embed=embed)
Exemple #26
0
    async def _birthday_reset(self, ctx: custom.Context) -> None:
        """
        Resets your birthday.
        """

        user_config = await self.bot.user_manager.get_config(ctx.author.id)
        await user_config.set_birthday()

        await ctx.reply(
            embed=utils.embed(
                colour=colours.GREEN,
                emoji=emojis.TICK,
                description="Birthday reset."
            )
        )
Exemple #27
0
    async def loop_queue(self, ctx: custom.Context) -> None:
        """
        Loops the queue.
        """

        if ctx.voice_client.queue.loop_mode != slate.QueueLoopMode.QUEUE:
            ctx.voice_client.queue.set_loop_mode(slate.QueueLoopMode.QUEUE)
        else:
            ctx.voice_client.queue.set_loop_mode(slate.QueueLoopMode.OFF)

        embed = utils.embed(
            colour=colours.GREEN,
            description=
            f"The queue looping mode is now **{ctx.voice_client.queue.loop_mode.name.title()}**.",
        )
        await ctx.reply(embed=embed)
Exemple #28
0
    async def seek(self, ctx: custom.Context, *, time: objects.Time) -> None:
        """
        Seeks to a position in the current track.

        **time**: The position to seek too.

        Valid time formats include:

        - 01:10:20 (hh:mm:ss)
        - 01:20 (mm:ss)
        - 20 (ss)
        - (h:m:s)
        - (hh:m:s)
        - (h:mm:s)
        - etc

        - 1 hour 20 minutes 30 seconds
        - 1 hour 20 minutes
        - 1 hour 30 seconds
        - 20 minutes 30 seconds
        - 20 minutes
        - 30 seconds
        - 1 hour 20 minutes and 30 seconds
        - 1h20m30s
        - 20m and 30s
        - 20s
        - etc
        """

        milliseconds = time.seconds * 1000

        if 0 < milliseconds > ctx.voice_client.current.length:
            raise exceptions.EmbedError(
                colour=colours.RED,
                description=
                f"That is not a valid amount of time, please choose a time between "
                f"**0s** and **{utils.format_seconds(ctx.voice_client.current.length // 1000, friendly=True)}**.",
            )

        await ctx.voice_client.set_position(milliseconds)

        embed = utils.embed(
            colour=colours.GREEN,
            description=
            f"The players position is now **{utils.format_seconds(ctx.voice_client.position // 1000, friendly=True)}**."
        )
        await ctx.reply(embed=embed)
Exemple #29
0
    async def tag_delete(self, ctx: custom.Context, *,
                         tag: objects.Tag) -> None:
        """
        Deletes a tag.

        **name**: The name of the tag to delete.
        """

        if tag.user_id != ctx.author.id:
            raise exceptions.EmbedError(colour=colours.RED,
                                        description="You do not own that tag.")

        await tag.delete()

        await ctx.reply(embed=utils.embed(
            colour=colours.GREEN,
            description=f"Deleted tag with name **{tag.name}**."))
Exemple #30
0
    async def todo_edit(self, ctx: custom.Context, todo: objects.Todo, *,
                        content: converters.TodoContentConverter) -> None:
        """
        Edits a todo.

        **todo_id**: The id of the todo to edit.
        **content**: The content of the todo.

        **Usage:**
        `l-todo edit 1 new content here`
        """

        await todo.change_content(content=str(content),
                                  jump_url=ctx.message.jump_url)

        await ctx.reply(embed=utils.embed(
            colour=colours.GREEN,
            description=f"Edited content of todo with id **{todo.id}**."))