Skip to main content

Discord Bot Development made easy!

Project description

disnake-super-utils


Documentation Secondary Documentation

A modern python module including many useful features that make discord bot programming extremely easy.
The documentation is not done. if you have any questions, feel free to ask them in our discord server.

Features

  • Very easy to use and user-friendly.
  • Object Oriented.
  • Modern Leveling Manager.
  • Modern Music/Audio playing manager. [Lavalink and FFmpeg support]
  • Modern Async Database Manager (SQLite, MongoDB, PostgreSQL, MySQL, MariaDB).
  • Modern Paginator.
  • Modern Reaction Manager.
  • Modern Economy Manager.
  • Modern Image Manager (PIL).
  • Modern Invite Tracker.
  • Modern Command Hinter.
  • Modern FiveM Server Parser.
  • Modern Birthday Manager.
  • Modern Prefix Manager.
  • Includes easy to use convertors.
  • Modern spotify client that is optimized for player fetching.
  • Modern Punishment Manager (Kick, Ban, Infractions, Mutes)
  • Modern Template Manager.
  • Modern CogManager that supports usage of managers in discord cogs.
  • Modern MessageFilter and AntiSpam.
  • Customizable ModMail Manager
  • Modern Youtube client that is optimized for player fetching.
  • And many more! (MORE COMING SOON!)

Installation

Installing disnakeSuperUtils is very easy.

python -m pip install disnakeSuperUtils

Examples

Leveling Example (With Role Manager)

import disnake
from disnake.ext import commands

import disnakeSuperUtils

bot = commands.Bot(command_prefix="-", intents=discord.Intents.all())
LevelingManager = disnakeSuperUtils.LevelingManager(bot, award_role=True)
ImageManager = (
    disnakeSuperUtils.ImageManager()
)  # LevelingManager uses ImageManager to create the rank command.


@bot.event
async def on_ready():
    database = disnakeSuperUtils.DatabaseManager.connect(...)
    await LevelingManager.connect_to_database(database, ["xp", "roles", "role_list"])

    print("Leveling manager is ready.", bot.user)


@LevelingManager.event()
async def on_level_up(message, member_data, roles):
    await message.reply(
        f"You are now level {await member_data.level()}"
        + (f", you have received the {roles[0]}" f" role." if roles else "")
    )


@bot.command()
async def rank(ctx):
    member_data = await LevelingManager.get_account(ctx.author)

    if not member_data:
        await ctx.send(f"I am still creating your account! please wait a few seconds.")
        return

    guild_leaderboard = await LevelingManager.get_leaderboard(ctx.guild)
    member = [x for x in guild_leaderboard if x.member == ctx.author.id]

    image = await ImageManager.create_leveling_profile(
        ctx.author,
        member_data,
        disnakeSuperUtils.Backgrounds.GALAXY,
        (127, 255, 0),
        guild_leaderboard.index(member[0]) + 1 if member else -1,
        outline=5,
    )
    await ctx.send(file=image)


@bot.command()
async def set_roles(ctx, interval: int, *roles: discord.Role):
    await LevelingManager.set_interval(ctx.guild, interval)
    await LevelingManager.set_roles(ctx.guild, roles)

    await ctx.send(
        f"Successfully set the interval to {interval} and role list to {', '.join(role.name for role in roles)}"
    )


@bot.command()
async def leaderboard(ctx):
    guild_leaderboard = await LevelingManager.get_leaderboard(ctx.guild)
    formatted_leaderboard = [
        f"Member: {x.member}, XP: {await x.xp()}" for x in guild_leaderboard
    ]

    await disnakeSuperUtils.PageManager(
        ctx,
        disnakeSuperUtils.generate_embeds(
            formatted_leaderboard,
            title="Leveling Leaderboard",
            fields=25,
            description=f"Leaderboard of {ctx.guild}",
        ),
    ).run()


bot.run("token")

Leveling Manager Example

Playing Example

from math import floor

from disnake.ext import commands

import disnakeSuperUtils
from disnakeSuperUtils import MusicManager
import disnake

client_id = ""
client_secret = ""

bot = commands.Bot(command_prefix="-", intents=discord.Intents.all())
# MusicManager = MusicManager(bot, spotify_support=False)


MusicManager = MusicManager(
    bot, client_id=client_id, client_secret=client_secret, spotify_support=True
)


# if using spotify support use this instead ^^^


@MusicManager.event()
async def on_music_error(ctx, error):
    raise error  # add your error handling here! Errors are listed in the documentation.


@MusicManager.event()
async def on_queue_end(ctx):
    print(f"The queue has ended in {ctx}")
    # You could wait and check activity, etc...


@MusicManager.event()
async def on_inactivity_disconnect(ctx):
    print(f"I have left {ctx} due to inactivity..")


@MusicManager.event()
async def on_play(ctx, player):
    await ctx.send(f"Playing {player}")


@bot.event
async def on_ready():
    # database = disnakeSuperUtils.DatabaseManager.connect(...)
    # await MusicManager.connect_to_database(database, ["playlists"])

    print("Music manager is ready.", bot.user)


@bot.command()
async def leave(ctx):
    if await MusicManager.leave(ctx):
        await ctx.send("Left Voice Channel")


@bot.command()
async def np(ctx):
    if player := await MusicManager.now_playing(ctx):
        duration_played = await MusicManager.get_player_played_duration(ctx, player)
        # You can format it, of course.

        await ctx.send(
            f"Currently playing: {player}, \n"
            f"Duration: {duration_played}/{player.duration}"
        )


@bot.command()
async def join(ctx):
    if await MusicManager.join(ctx):
        await ctx.send("Joined Voice Channel")


@bot.group(invoke_without_command=True)
async def playlists(ctx, user: discord.User):
    user_playlists = await MusicManager.get_user_playlists(user)

    formatted_playlists = [
        f"ID: '{user_playlist.id}'\nTitle: '{user_playlist.playlist.title}'\nTotal Songs: {len(user_playlist.playlist.songs)}"
        for user_playlist in user_playlists
    ]

    embeds = disnakeSuperUtils.generate_embeds(
        formatted_playlists,
        f"Playlists of {user}",
        f"Shows {user.mention}'s playlists.",
        25,
        string_format="{}",
    )

    page_manager = disnakeSuperUtils.PageManager(ctx, embeds, public=True)
    await page_manager.run()


@playlists.command()
async def add(ctx, url: str):
    added_playlist = await MusicManager.add_playlist(ctx.author, url)

    if not added_playlist:
        await ctx.send("Playlist URL not found!")
        return

    await ctx.send(f"Playlist added with ID {added_playlist.id}")


@playlists.command()
async def play(ctx, playlist_id: str):
    # This command is just an example, and not something you should do.
    # The saved playlist system is supposed to provide fast, easy and simple playing, and the user should not look for
    # the right playlist id before playing, as that defeats the whole point.
    # Instead of playing using a playlist id, I recommend playing using indexes.
    # Please, if you are playing using indexes, find the playlist id you need by getting all the user's playlists
    # and then finding the id from there.
    # Find the user's playlists using MusicManager.get_user_playlists(ctx.author, partial=True).
    # Make sure partial is True to speed up the fetching progress (incase you want to access the playlist data,
    # you can set it to False, of course).
    # Using these playlists, find the id the user wants, and play it (or whatever else you want to do with it).
    # Be creative!

    user_playlist = await MusicManager.get_playlist(ctx.author, playlist_id)

    if not user_playlist:
        await ctx.send("That playlist does not exist!")
        return

    if not ctx.voice_client or not ctx.voice_client.is_connected():
        await MusicManager.join(ctx)

    async with ctx.typing():
        players = await MusicManager.create_playlist_players(
            user_playlist.playlist, ctx.author
        )

    if players:
        if await MusicManager.queue_add(
            players=players, ctx=ctx
        ) and not await MusicManager.play(ctx):
            await ctx.send(f"Added playlist {user_playlist.playlist.title}")

    else:
        await ctx.send("Query not found.")


@playlists.command()
async def remove(ctx, playlist_id: str):
    user_playlist = await MusicManager.get_playlist(ctx.author, playlist_id)

    if not user_playlist:
        await ctx.send(f"Playlist with id {playlist_id} is not found.")
        return

    await user_playlist.delete()
    await ctx.send(f"Playlist {user_playlist.playlist.title} has been deleted")


@bot.command()
async def play(ctx, *, query: str):
    if not ctx.voice_client or not ctx.voice_client.is_connected():
        await MusicManager.join(ctx)

    async with ctx.typing():
        players = await MusicManager.create_player(query, ctx.author)

    if players:
        if await MusicManager.queue_add(
            players=players, ctx=ctx
        ) and not await MusicManager.play(ctx):
            await ctx.send("Added to queue")

    else:
        await ctx.send("Query not found.")


@bot.command()
async def lyrics(ctx, query: str = None):
    if response := await MusicManager.lyrics(ctx, query):
        title, author, query_lyrics = response

        splitted = query_lyrics.split("\n")
        res = []
        current = ""
        for i, split in enumerate(splitted):
            if len(splitted) <= i + 1 or len(current) + len(splitted[i + 1]) > 1024:
                res.append(current)
                current = ""
                continue
            current += split + "\n"

        page_manager = disnakeSuperUtils.PageManager(
            ctx,
            [
                discord.Embed(
                    title=f"Lyrics for '{title}' by '{author}', (Page {i + 1}/{len(res)})",
                    description=x,
                )
                for i, x in enumerate(res)
            ],
            public=True,
        )
        await page_manager.run()
    else:
        await ctx.send("No lyrics found.")


@bot.command()
async def pause(ctx):
    if await MusicManager.pause(ctx):
        await ctx.send("Player paused.")


@bot.command()
async def resume(ctx):
    if await MusicManager.resume(ctx):
        await ctx.send("Player resumed.")


@bot.command()
async def volume(ctx, volume: int):
    await MusicManager.volume(ctx, volume)


@bot.command()
async def loop(ctx):
    is_loop = await MusicManager.loop(ctx)

    if is_loop is not None:
        await ctx.send(f"Looping toggled to {is_loop}")


@bot.command()
async def shuffle(ctx):
    is_shuffle = await MusicManager.shuffle(ctx)

    if is_shuffle is not None:
        await ctx.send(f"Shuffle toggled to {is_shuffle}")


@bot.command()
async def autoplay(ctx):
    is_autoplay = await MusicManager.autoplay(ctx)

    if is_autoplay is not None:
        await ctx.send(f"Autoplay toggled to {is_autoplay}")


@bot.command()
async def queueloop(ctx):
    is_loop = await MusicManager.queueloop(ctx)

    if is_loop is not None:
        await ctx.send(f"Queue looping toggled to {is_loop}")


@bot.command()
async def complete_queue(ctx):
    if ctx_queue := await MusicManager.get_queue(ctx):
        formatted_queue = [
            f"Title: '{x.title}'\nRequester: {x.requester and x.requester.mention}\n"
            f"Position: {i - ctx_queue.pos}"
            for i, x in enumerate(ctx_queue.queue)
        ]

        num_of_fields = 25

        embeds = disnakeSuperUtils.generate_embeds(
            formatted_queue,
            "Complete Song Queue",
            "Shows the complete song queue.",
            num_of_fields,
            string_format="{}",
        )

        page_manager = disnakeSuperUtils.PageManager(
            ctx, embeds, public=True, index=floor(ctx_queue.pos / 25)
        )
        await page_manager.run()


@bot.command()
async def goto(ctx, position: int):
    if ctx_queue := await MusicManager.get_queue(ctx):
        new_pos = ctx_queue.pos + position
        if not 0 <= new_pos < len(ctx_queue.queue):
            await ctx.send("Position is out of bounds.")
            return

        await MusicManager.goto(ctx, new_pos)
        await ctx.send(f"Moved to position {position}")


@bot.command()
async def history(ctx):
    if ctx_queue := await MusicManager.get_queue(ctx):
        formatted_history = [
            f"Title: '{x.title}'\nRequester: {x.requester and x.requester.mention}"
            for x in ctx_queue.history
        ]

        embeds = disnakeSuperUtils.generate_embeds(
            formatted_history,
            "Song History",
            "Shows all played songs",
            25,
            string_format="{}",
        )

        page_manager = disnakeSuperUtils.PageManager(ctx, embeds, public=True)
        await page_manager.run()


@bot.command()
async def skip(ctx, index: int = None):
    await MusicManager.skip(ctx, index)


@bot.command()
async def queue(ctx):
    if ctx_queue := await MusicManager.get_queue(ctx):
        formatted_queue = [
            f"Title: '{x.title}\nRequester: {x.requester and x.requester.mention}"
            for x in ctx_queue.queue[ctx_queue.pos + 1 :]
        ]

        embeds = disnakeSuperUtils.generate_embeds(
            formatted_queue,
            "Queue",
            f"Now Playing: {await MusicManager.now_playing(ctx)}",
            25,
            string_format="{}",
        )

        page_manager = disnakeSuperUtils.PageManager(ctx, embeds, public=True)
        await page_manager.run()


@bot.command()
async def rewind(ctx, index: int = None):
    await MusicManager.previous(ctx, index, no_autoplay=True)


@bot.command()
async def ls(ctx):
    if queue := await MusicManager.get_queue(ctx):
        loop = queue.loop
        loop_status = None

        if loop == disnakeSuperUtils.Loops.LOOP:
            loop_status = "Looping enabled."

        elif loop == disnakeSuperUtils.Loops.QUEUE_LOOP:
            loop_status = "Queue looping enabled."

        elif loop == disnakeSuperUtils.Loops.NO_LOOP:
            loop_status = "No loop enabled."

        if loop_status:
            await ctx.send(loop_status)


@bot.command()
async def move(ctx, player_index: int, index: int):
    await MusicManager.move(ctx, player_index, index)


bot.run("token")

MusicManager Example

More examples are listed in the examples folder.

Known Issues

  • Removing an animated emoji wont be recognized as a reaction role, as it shows up as not animated for some reason, breaking the reaction matcher. (Discord API Related)
  • Leveling might call the on_level_up event multiple times, resulting in duplicate messages, caused by duplicate records in the leveling table. (Fixed)

Support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

disnakeSuperUtils-0.9.2.tar.gz (1.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

disnakeSuperUtils-0.9.2-py3-none-any.whl (1.8 MB view details)

Uploaded Python 3

File details

Details for the file disnakeSuperUtils-0.9.2.tar.gz.

File metadata

  • Download URL: disnakeSuperUtils-0.9.2.tar.gz
  • Upload date:
  • Size: 1.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.62.3 importlib-metadata/4.10.1 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.9.10

File hashes

Hashes for disnakeSuperUtils-0.9.2.tar.gz
Algorithm Hash digest
SHA256 6330970a8bc91333d6b119bea5f9549c2f1c2b401ba351d53b8a23973f4b5cec
MD5 e6699bb3c21a9cdb61cebf0daca525f8
BLAKE2b-256 dc3c2654e5a69de5cea9dd3ef24bef39dadfa25725aeb68870a09fb5684215b5

See more details on using hashes here.

File details

Details for the file disnakeSuperUtils-0.9.2-py3-none-any.whl.

File metadata

  • Download URL: disnakeSuperUtils-0.9.2-py3-none-any.whl
  • Upload date:
  • Size: 1.8 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.62.3 importlib-metadata/4.10.1 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.9.10

File hashes

Hashes for disnakeSuperUtils-0.9.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e4c6b9cac0fbd9b94f273b60ba3b9d20c94319248b0609fba08c0fa3ab5d4e1d
MD5 27980494d867491bfb6e29bc1977d034
BLAKE2b-256 c9c042924f87e1d1284cebf289ea9159d01ce96371b33eafab1de173e75a4836

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page