Skip to content

Commit

Permalink
chore: update readme
Browse files Browse the repository at this point in the history
Release-As: 1.0.0
  • Loading branch information
ooliver1 committed Feb 7, 2023
1 parent b59d8c0 commit 5763b4a
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 223 deletions.
73 changes: 65 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,85 @@
[![Releases](https://custom-icon-badges.demolab.com/github/v/release/ooliver1/mafic?display_name=tag&include_prereleases&sort=semver&logo=commit&color=c25db8)](https://github.com/ooliver1/mafic/releases "Mafic Releases")
[![Dependency Version Status](https://custom-icon-badges.demolab.com/librariesio/github/ooliver1/mafic?logo=versions&color=f062a4)](https://github.com/ooliver1/mafic/blob/master/pyproject.toml "Poetry Dependencies")
[![Lint Workflow Status](https://custom-icon-badges.demolab.com/github/workflow/status/ooliver1/mafic/lint?label=lint&logo=codescan-checkmark&color=ff738c)](https://github.com/ooliver1/mafic/actions/workflows/lint.yml "Lint Workflow")
[![Lines of Code](https://custom-icon-badges.demolab.com/tokei/lines/github/ooliver1/mafic?logo=quote&color=ff9075)](https://github.com/ooliver1/mafic/tree/master/mafic "Mafic Module Tree")
[![PyPI - Status](https://img.shields.io/pypi/status/mafic?color=ff9075&label=PyPI&logo=pypi&logoColor=white)](https://pypi.org/project/mafic "Mafic PyPI Project")
[![Open Issues](https://custom-icon-badges.demolab.com/github/issues-raw/ooliver1/mafic?logo=issue-opened&color=ffb263)](https://github.com/ooliver1/mafic/issues "Open Issues")
[![Open PRs](https://custom-icon-badges.demolab.com/github/issues-pr-raw/ooliver1/mafic?logo=git-pull-request&color=ffd55f)](https://github.com/ooliver1/mafic/pulls "Open Pull Requests")
[![Last Commit](https://custom-icon-badges.demolab.com/github/last-commit/ooliver1/mafic?logo=git-commit&color=f9f871)](https://github.com/ooliver1/mafic/commits/master)
[![Read the Docs](https://img.shields.io/readthedocs/mafic?logo=read%20the%20docs&logoColor=white&color=f9f871)](https://mafic.readthedocs.io/en/latest/)

A properly typehinted lavalink client for discord.py, nextcord, disnake and py-cord.

## Installation

Mafic, as it is in alpha, does not have a stable release on PyPI. To install it, you must use the following command:

```bash
pip install git+https://github.com/ooliver1/mafic
pip install mafic
```

> **Note**
> Use `python -m`, `py -m`, `python3 -m` or similar if that is how you install packages.
> Generally windows uses `py -m pip` and linux uses `python3 -m pip`
## Documentation

[Read the docs](https://mafic.readthedocs.io/en/latest/).

## Features

- Fully customisable node balancing.
- Multi-node support.
- Filters.
- Full API coverage.
- Properly typehinted for Pyright strict.

## Usage

> **Warning**
> Mafic is in public alpha, do not use in production.
Go to the [Lavalink Repository](https://github.com/freyacodes/lavalink#server-configuration)
to set up a Lavalink node.

```python
import os

import mafic
import nextcord
from nextcord.ext import commands


class MyBot(commands.Bot):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.pool = mafic.NodePool(self)
self.loop.create_task(self.add_nodes())

async def add_nodes(self):
await self.pool.create_node(
host="127.0.0.1",
port=2333,
label="MAIN",
password="<password>",
)

There are no examples as of now. For development purposes, `test_bot` contains the start of a bot using mafic.

bot = MyBot(intents=nextcord.Intents(guilds=True, voice_states=True))


@bot.slash_command(dm_permission=False)
async def play(inter: nextcord.Interaction, query: str):
if not inter.guild.voice_client:
player = await inter.user.voice.channel.connect(cls=mafic.Player)
else:
player = inter.guild.voice_client

tracks = await player.fetch_tracks(query)

if not tracks:
return await inter.send("No tracks found.")

track = tracks[0]

await player.play(track)

await inter.send(f"Playing {track.title}.")


bot.run(...)
```
123 changes: 123 additions & 0 deletions examples/simple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""A simple example of using Mafic."""

from __future__ import annotations

import traceback
from logging import DEBUG, getLogger
from os import getenv
from typing import Any

from nextcord import Intents, Interaction, Member
from nextcord.abc import Connectable
from nextcord.ext import commands

from mafic import NodePool, Player, Playlist, Track, TrackEndEvent

getLogger("mafic").setLevel(DEBUG)


class Bot(commands.Bot):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)

self.ready_ran = False
self.pool = NodePool(self)

async def on_ready(self):
if self.ready_ran:
return

await self.pool.create_node(
host="127.0.0.1",
port=6969,
label="MAIN",
password="haha",
)

self.ready_ran = True


bot = Bot(intents=Intents(guilds=True, voice_states=True))


class MyPlayer(Player[Bot]):
def __init__(self, client: Bot, channel: Connectable) -> None:
super().__init__(client, channel)

# Mafic does not provide a queue system right now, low priority.
self.queue: list[Track] = []


@bot.slash_command(dm_permission=False)
async def join(inter: Interaction[Bot]):
"""Join your voice channel."""

assert isinstance(inter.user, Member)

if not inter.user.voice or not inter.user.voice.channel:
return await inter.response.send_message("You are not in a voice channel.")

channel = inter.user.voice.channel

# This apparently **must** only be `Client`.
await channel.connect(cls=MyPlayer) # pyright: ignore[reportGeneralTypeIssues]
await inter.send(f"Joined {channel.mention}.")


@bot.slash_command(dm_permission=False)
async def play(inter: Interaction[Bot], query: str):
"""Play a song.
query:
The song to search or play.
"""

assert inter.guild is not None

if not inter.guild.voice_client:
await join(inter)

player: MyPlayer = (
inter.guild.voice_client
) # pyright: ignore[reportGeneralTypeIssues]

tracks = await player.fetch_tracks(query)

if not tracks:
return await inter.send("No tracks found.")

if isinstance(tracks, Playlist):
tracks = tracks.tracks
if len(tracks) > 1:
player.queue.extend(tracks[1:])

track = tracks[0]

await player.play(track)

await inter.send(f"Playing {track}")


@bot.listen()
async def on_track_end(event: TrackEndEvent):
assert isinstance(event.player, MyPlayer)

if event.player.queue:
await event.player.play(event.player.queue.pop(0))


@bot.event
async def on_application_command_error(inter: Interaction[Bot], error: Exception):
traceback.print_exception(type(error), error, error.__traceback__)
await inter.send(f"An error occurred: {error}")


STATS = """```
Uptime: {uptime}
Memory: {used:.0f}MiB : {free:.0f}MiB / {allocated:.0f}MiB -- {reservable:.0f}MiB
CPU: {system_load:.2f}% : {lavalink_load:.2f}%
Players: {player_count}
Playing Players: {playing_player_count}
```"""

bot.run(getenv("TOKEN"))
Loading

0 comments on commit 5763b4a

Please sign in to comment.