Merge pull request #11 from ChocoMeow/beta

Vocard v2.6.5a Update: bug fixes, and code clean-up
This commit is contained in:
Choco
2023-07-22 09:30:09 +08:00
committed by GitHub
11 changed files with 140 additions and 115 deletions

View File

@@ -6,6 +6,7 @@ import textwrap
import traceback
import function as func
from typing import Tuple
from discord import app_commands
from discord.ext import commands
from function import (
@@ -25,7 +26,7 @@ class Admin(commands.Cog, name="settings"):
self.bot = bot
self.description = "This category is only available to admin permissions on the server."
def get_settings(self, ctx: commands.Context) -> dict:
def get_settings(self, ctx: commands.Context) -> Tuple[voicelink.Player, dict]:
player: voicelink.Player = ctx.guild.voice_client
if not player:
settings = get_settings(ctx.guild.id)
@@ -34,9 +35,11 @@ class Admin(commands.Cog, name="settings"):
return player, settings
@commands.hybrid_group(name="settings",
aliases=get_aliases("settings"),
invoke_without_command=True)
@commands.hybrid_group(
name="settings",
aliases=get_aliases("settings"),
invoke_without_command=True
)
async def settings(self, ctx: commands.Context):
view = HelpView(self.bot, ctx.author)
embed = view.build_embed(self.qualified_name)
@@ -101,8 +104,8 @@ class Admin(commands.Cog, name="settings"):
async def queue(self, ctx: commands.Context, mode: str):
"Change to another type of queue mode."
player, settings = self.get_settings(ctx)
if mode.capitalize() not in ["FairQueue", "Queue"]:
mode = "Queue"
mode = "FairQueue" if mode.lower() == "fairqueue" else "Queue"
settings["queueType"] = mode
update_settings(ctx.guild.id, {"queueType": mode})
await ctx.send(get_lang(ctx.guild.id, "setqueue").format(mode))
@@ -211,10 +214,10 @@ class Admin(commands.Cog, name="settings"):
player, settings = self.get_settings(ctx)
toggle = settings.get('duplicateTrack', False)
if player:
player.queue._duplicateTrack = not toggle
player.queue._allow_duplicate = not toggle
update_settings(ctx.guild.id, {'duplicateTrack': not toggle})
toggle = get_lang(ctx.guild.id, "enabled" if not toggle else "disabled")
toggle = get_lang(ctx.guild.id, "enabled" if toggle else "disabled")
return await ctx.send(get_lang(ctx.guild.id, "toggleDuplicateTrack").format(toggle))
@settings.command(name="customcontroller", aliases=get_aliases("customcontroller"))
@@ -234,7 +237,7 @@ class Admin(commands.Cog, name="settings"):
if interaction.user.id not in func.settings.bot_access_user:
return await interaction.response.send_message("You are not able to use this command!")
def clear_code(content):
def clear_code(content: str):
if content.startswith("```") and content.endswith("```"):
return "\n".join(content.split("\n")[1:])[:-3]
else:

View File

@@ -322,8 +322,9 @@ class Basic(commands.Cog):
await ctx.send(player.get_msg('skipped').format(ctx.author))
if player.queue._repeat == 1:
await player.queue.set_repeat("off")
if player.queue._repeat.mode == voicelink.LoopType.track:
await player.set_repeat(voicelink.LoopType.off.name)
await player.stop()
@commands.hybrid_command(name="back", aliases=get_aliases("back"))
@@ -357,8 +358,8 @@ class Basic(commands.Cog):
await ctx.send(player.get_msg('backed').format(ctx.author))
if player.queue._repeat == 1:
await player.queue.set_repeat("off")
if player.queue._repeat.mode == voicelink.LoopType.track:
await player.set_repeat(voicelink.LoopType.off.name)
@commands.hybrid_command(name="seek", aliases=get_aliases("seek"))
@app_commands.describe(position="Input position. Exmaple: 1:20.")
@@ -456,7 +457,7 @@ class Basic(commands.Cog):
track_ids = bytes.split(b"\n")[-1]
track_ids = track_ids.decode().split(",")
tracks = [voicelink.Track(track_id=track_id, info=voicelink.decode(track_id), requester=ctx.author) for track_id in track_ids]
tracks = (voicelink.Track(track_id=track_id, info=voicelink.decode(track_id), requester=ctx.author) for track_id in track_ids)
if not tracks:
return await ctx.send(player.get_msg('noTrackFound'))

View File

@@ -26,7 +26,10 @@ if not (tokens.mongodb_name and tokens.mongodb_url):
try:
mongodb = MongoClient(host=tokens.mongodb_url, serverSelectionTimeoutMS=5000)
mongodb.server_info()
if tokens.mongodb_name not in mongodb.list_database_names():
raise Exception(f"{tokens.mongodb_name} does not exist in your mongoDB!")
print("Successfully connected to MongoDB!")
except Exception as e:
raise Exception("Not able to connect MongoDB! Reason:", e)

View File

@@ -88,7 +88,7 @@ class Vocard(commands.Bot):
elif isinstance(error, (commands.CommandOnCooldown, commands.MissingPermissions, commands.RangeError, commands.BadArgument)):
pass
elif isinstance(error, commands.MissingRequiredArgument, commands.MissingRequiredAttachment):
elif isinstance(error, (commands.MissingRequiredArgument, commands.MissingRequiredAttachment)):
command = f" Correct Usage: {ctx.prefix}" + (f"{ctx.command.parent.qualified_name} " if ctx.command.parent else "") + f"{ctx.command.name} {ctx.command.signature}"
position = command.find(f"<{ctx.current_parameter.name}>") + 1
error = f"```css\n[You are missing argument!]\n{command}\n" + " " * position + "^" * len(ctx.current_parameter.name) + "```"

View File

@@ -3,7 +3,7 @@ from io import BytesIO
root_dir = os.path.dirname(os.path.abspath(__file__))
install_pack_dir = os.path.join(root_dir, "Vocard.zip")
__version__ = "v2.6.5"
__version__ = "v2.6.5a"
def checkVersion(withMsg = False):
resp = requests.get("https://api.github.com/repos/ChocoMeow/Vocard/releases/latest")

View File

@@ -22,6 +22,7 @@ SOFTWARE.
"""
import discord
import voicelink
import function as func
from discord.ext import commands
@@ -40,7 +41,7 @@ def key(interaction: discord.Interaction):
class Back(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="⏮️", label=player.get_msg('buttonBack'), style=style, disabled=False if self.player.queue.history() or not self.player.current else True, row=row)
async def callback(self, interaction: discord.Interaction):
@@ -63,12 +64,12 @@ class Back(discord.ui.Button):
await interaction.response.send_message(self.player.get_msg("backed").format(interaction.user))
if self.player.queue._repeat == 1:
await self.player.set_repeat("off")
if self.player.queue._repeat.mode == voicelink.LoopType.track:
await self.player.set_repeat(voicelink.LoopType.off.name)
class Resume(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="⏸️", label=player.get_msg('buttonPause'), style=style, disabled=False if self.player.current else True, row=row)
async def callback(self, interaction: discord.Interaction):
@@ -107,7 +108,7 @@ class Resume(discord.ui.Button):
class Skip(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="⏭️", label=player.get_msg('buttonSkip'), style=style, row=row)
async def callback(self, interaction: discord.Interaction):
@@ -127,13 +128,13 @@ class Skip(discord.ui.Button):
await interaction.response.send_message(self.player.get_msg("skipped").format(interaction.user))
if self.player.queue._repeat == 1:
await self.player.set_repeat("off")
if self.player.queue._repeat.mode == voicelink.LoopType.track:
await self.player.set_repeat(voicelink.LoopType.off.name)
await self.player.stop()
class Stop(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="⏹️", label=player.get_msg('buttonLeave'), style=style, row=row)
async def callback(self, interaction: discord.Interaction):
if not self.player.is_privileged(interaction.user):
@@ -151,7 +152,7 @@ class Stop(discord.ui.Button):
class Add(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="❤️", style=style, disabled=False if self.player.current else True, row=row)
async def callback(self, interaction: discord.Interaction):
@@ -177,7 +178,7 @@ class Add(discord.ui.Button):
class Loop(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="🔁", label=player.get_msg('buttonLoop'), style=style, row=row)
async def callback(self, interaction: discord.Interaction):
@@ -189,7 +190,7 @@ class Loop(discord.ui.Button):
class VolumeUp(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="🔊", label=player.get_msg('buttonVolumeUp'), style=style, row=row)
async def callback(self, interaction: discord.Interaction):
@@ -203,7 +204,7 @@ class VolumeUp(discord.ui.Button):
class VolumeDown(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="🔉", label=player.get_msg('buttonVolumeDown'), style=style, row=row)
async def callback(self, interaction: discord.Interaction):
@@ -217,7 +218,7 @@ class VolumeDown(discord.ui.Button):
class VolumeMute(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="🔇" if player.volume else "🔈",
label=player.get_msg('buttonVolumeMute' if player.volume else "buttonVolumeUnmute"),
style=style, row=row)
@@ -241,7 +242,7 @@ class VolumeMute(discord.ui.Button):
class AutoPlay(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="💡",
label=player.get_msg('buttonAutoPlay'),
style=style, row=row)
@@ -259,7 +260,7 @@ class AutoPlay(discord.ui.Button):
class Shuffle(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="🔀",
label=player.get_msg('buttonShuffle'),
style=style, row=row)
@@ -280,7 +281,7 @@ class Shuffle(discord.ui.Button):
class Forward(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="",
label=player.get_msg('buttonForward'),
disabled=False if self.player.current else True,
@@ -298,7 +299,7 @@ class Forward(discord.ui.Button):
class Rewind(discord.ui.Button):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
super().__init__(emoji="",
label=player.get_msg('buttonRewind'),
disabled=False if self.player.current else True,
@@ -319,7 +320,7 @@ class Rewind(discord.ui.Button):
class Tracks(discord.ui.Select):
def __init__(self, player, style, row):
self.player = player
self.player: voicelink.Player = player
options = []
for index, track in enumerate(self.player.queue.tracks(), start=1):
@@ -370,7 +371,7 @@ class InteractiveController(discord.ui.View):
def __init__(self, player):
super().__init__(timeout=None)
self.player = player
self.player: voicelink.Player = player
for row, btnRow in enumerate(func.settings.controller.get("default_buttons")):
for btn in btnRow:
color = ""
@@ -385,7 +386,7 @@ class InteractiveController(discord.ui.View):
self.cooldown = commands.CooldownMapping.from_cooldown(2.0, 10.0, key)
async def interaction_check(self, interaction):
async def interaction_check(self, interaction: discord.Interaction):
if not self.player.node._available:
await interaction.response.send_message(self.player.get_msg("nodeReconnect"), ephemeral=True)
return False

View File

@@ -26,7 +26,7 @@ __author__ = 'Vocard Development, Choco'
__license__ = "MIT"
__copyright__ = "Copyright 2023 (c) Vocard Development, Choco"
from .enums import SearchType
from .enums import SearchType, LoopType
from .events import *
from .exceptions import *
from .filters import *

View File

@@ -23,6 +23,18 @@ SOFTWARE.
from enum import Enum, auto
class LoopType(Enum):
"""The enum for the different loop types for Voicelink
LoopType.off: 1
LoopType.track: 2
LoopType.queue: 3
"""
off = auto()
track = auto()
queue = auto()
class SearchType(Enum):
"""The enum for the different search types for Voicelink.

View File

@@ -39,10 +39,8 @@ from discord import (
Client,
Guild,
VoiceChannel,
VoiceProtocol,
StageChannel,
VoiceProtocol,
Member,
Embed,
ui,
Message,
Interaction
@@ -50,9 +48,9 @@ from discord import (
from discord.ext import commands
from . import events
from .enums import SearchType
from .enums import SearchType, LoopType
from .events import VoicelinkEvent, TrackEndEvent, TrackStartEvent
from .exceptions import VoicelinkException, FilterInvalidArgument, TrackInvalidPosition, TrackLoadError, FilterTagAlreadyInUse
from .exceptions import VoicelinkException, FilterInvalidArgument, TrackInvalidPosition, TrackLoadError, FilterTagAlreadyInUse, DuplicateTrack
from .filters import Filter, Filters
from .objects import Track
from .pool import Node, NodePool
@@ -565,12 +563,20 @@ class Player(VoiceProtocol):
async def add_track(self, raw_tracks: Union[Track, List[Track]], at_font: bool = False) -> int:
tracks = []
_duplicate_tracks = () if self.queue._allow_duplicate else (track.uri for track in self.queue._queue)
try:
if (isList := isinstance(raw_tracks, List)):
for track in raw_tracks:
if track.uri in _duplicate_tracks:
continue
self.queue.put_at_front(track) if at_font else self.queue.put(track)
tracks.append(track)
else:
if raw_tracks.uri in _duplicate_tracks:
raise DuplicateTrack(self.get_msg("voicelinkDuplicateTrack"))
position = self.queue.put_at_front(raw_tracks) if at_font else self.queue.put(raw_tracks)
tracks.append(raw_tracks)
finally:
@@ -605,7 +611,7 @@ class Player(VoiceProtocol):
self._volume = volume
return self._volume
async def shuffle(self, queue_type: str, requester: Member = None):
async def shuffle(self, queue_type: str, requester: Member = None) -> None:
replacement = self.queue.tracks() if queue_type == "queue" else self.queue.history()
if len(replacement) < 3:
raise VoicelinkException(self.get_msg('shuffleError'))
@@ -623,19 +629,17 @@ class Player(VoiceProtocol):
}
}, requester)
async def set_repeat(self, mode:str = None):
async def set_repeat(self, mode: str = None) -> str:
if not mode:
mode = self.queue._repeat_mode.get((self.queue._repeat + 1)%len(self.queue._repeat_mode), 'off')
mode = self.queue._repeat.next().name
is_found = False
for i, m in self.queue._repeat_mode.items():
if m == mode.lower():
for type in LoopType:
if type.name.lower() == mode.lower():
self.queue._repeat.set_mode(type)
is_found = True
self.queue._repeat = i
if i == 2:
self._repeat_position = self._position - 1
break
if not is_found:
raise VoicelinkException("Invalid repeat mode.")

View File

@@ -21,41 +21,60 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
from .exceptions import QueueFull, OutofList, DuplicateTrack
from .exceptions import QueueFull, OutofList
from .objects import Track
from .enums import LoopType
from typing import Optional, Tuple, List, Callable
from itertools import cycle
from discord import Member
class LoopTypeCycle:
def __init__(self) -> None:
self._cycle = cycle(LoopType)
self.current = next(self._cycle)
def next(self) -> LoopType:
self.current = next(self._cycle)
return self.current
def set_mode(self, value: LoopType) -> LoopType:
while next(self._cycle) != value:
pass
self.current = value
return value
@property
def mode(self) -> LoopType:
return self.current
def __str__(self) -> str:
return self.current.name.capitalize()
class Queue:
def __init__(self, size: int, duplicate_track: bool, get_msg):
def __init__(self, size: int, allow_duplicate: bool, get_msg: Callable[[str], str]) -> None:
self._queue = []
self._position = 0
self._size = size
self._repeat = 0
self._repeat = LoopTypeCycle()
self._repeat_position = 0
self._duplicate_track = duplicate_track
self._repeat_mode = {
0: "off",
1: "track",
2: "queue",
}
self._allow_duplicate = allow_duplicate
self.get_msg = get_msg
def get(self):
def get(self) -> Optional[Track]:
track = None
try:
track = self._queue[self._position -
1 if self._repeat == 1 else self._position]
if self._repeat != 1:
track = self._queue[self._position - 1 if self._repeat.mode == LoopType.track else self._position]
if self._repeat.mode != LoopType.track:
self._position += 1
except:
if self._repeat == 2:
if self._repeat.mode == LoopType.queue:
try:
track = self._queue[self._repeat_position]
self._position = self._repeat_position + 1
except IndexError:
self._repeat = 0
self._repeat.set_mode(LoopType.off)
return track
@@ -63,61 +82,49 @@ class Queue:
if self.count >= self._size:
raise QueueFull(self.get_msg("voicelinkQueueFull").format(self._size))
if not self._duplicate_track:
if item.uri in [track.uri for track in self._queue]:
raise DuplicateTrack(self.get_msg("voicelinkDuplicateTrack"))
self._queue.append(item)
return self.count
def put_at_front(self, item: Track):
def put_at_front(self, item: Track) -> int:
if self.count >= self._size:
raise QueueFull(self.get_msg("voicelinkQueueFull").format(self._size))
if not self._duplicate_track:
if item.uri in [track.uri for track in self._queue]:
raise DuplicateTrack(self.get_msg("voicelinkDuplicateTrack"))
self._queue.insert(self._position, item)
return 1
def put_at_index(self, index: int, item: Track):
def put_at_index(self, index: int, item: Track) -> None:
if self.count >= self._size:
raise QueueFull(self.get_msg("voicelinkQueueFull").format(self._size))
if not self._duplicate_track:
if item.uri in [track.uri for track in self._queue]:
raise DuplicateTrack(self.get_msg("voicelinkDuplicateTrack"))
return self._queue.insert(self._position - 1 + index, item)
def skipto(self, index: int):
def skipto(self, index: int) -> None:
if not 0 < index <= self.count:
raise OutofList(self.get_msg("voicelinkOutofList"))
else:
self._position += index - 1
def backto(self, index: int):
def backto(self, index: int) -> None:
if not self._position - index >= 0:
raise OutofList(self.get_msg("voicelinkOutofList"))
else:
self._position -= index
def history_clear(self, is_playing: bool):
def history_clear(self, is_playing: bool) -> None:
self._queue[:self._position - 1 if is_playing else self._position] = []
self._position = 1 if is_playing else 0
def clear(self):
def clear(self) -> None:
del self._queue[self._position:]
def replace(self, queue_type: str, replacement: list):
def replace(self, queue_type: str, replacement: list) -> None:
if queue_type == "queue":
self.clear()
self._queue += replacement
elif queue_type == "history":
self._queue[:self._position] = replacement
def swap(self, num1: int, num2: int):
def swap(self, num1: int, num2: int) -> Tuple[Track, Track]:
try:
pos = self._position - 1
self._queue[pos + num1], self._queue[pos + num2] = self._queue[pos + num2], self._queue[pos + num1]
@@ -125,7 +132,7 @@ class Queue:
except IndexError:
raise OutofList(self.get_msg("voicelinkOutofList"))
def move(self, target: int, to: int):
def move(self, target: int, to: int) -> Optional[Track]:
if not 0 < target <= self.count or not 0 < to:
raise OutofList(self.get_msg("voicelinkOutofList"))
@@ -137,20 +144,18 @@ class Queue:
except:
raise OutofList(self.get_msg("voicelinkOutofList"))
def remove(self, index: int, index2: int = None, member: Member = None):
def remove(self, index: int, index2: int = None, member: Member = None) -> Optional[List[Track]]:
pos = self._position - 1
if index2 is None:
index2 = index
if index2 < index:
temp = index
index = index2
index2 = temp
elif index2 < index:
index, index2 = index2, index
try:
count = []
for i, track in enumerate(self._queue[pos + index: pos + index2 + 1], start=0):
for i, track in enumerate(self._queue[pos + index: pos + index2 + 1]):
if member:
if track.requester != member:
continue
@@ -162,46 +167,41 @@ class Queue:
except:
raise OutofList(self.get_msg("voicelinkOutofList"))
def history(self, incTrack: bool = False) -> list:
def history(self, incTrack: bool = False) -> List[Track]:
if incTrack:
return self._queue[:self._position]
return self._queue[:self._position - 1]
def tracks(self, incTrack: bool = False):
def tracks(self, incTrack: bool = False) -> List[Track]:
if incTrack:
return self._queue[self._position - 1:]
return self._queue[self._position:]
@property
def count(self):
def count(self) -> int:
return len(self._queue[self._position:])
@property
def repeat(self) -> str:
return self._repeat.mode.name.capitalize()
@property
def repeat(self):
return self._repeat_mode.get(self._repeat, "Off").capitalize()
@property
def is_empty(self):
def is_empty(self) -> bool:
try:
self._queue[self._position]
except:
return True
return False
class FairQueue(Queue):
def __init__(self, size: int, duplicate_track: bool, get_msg):
super().__init__(size, duplicate_track, get_msg)
def __init__(self, size: int, allow_duplicate: bool, get_msg) -> None:
super().__init__(size, allow_duplicate, get_msg)
self._set = set()
async def put(self, item: Track) -> int:
def put(self, item: Track) -> int:
if len(self._queue) >= self._size:
raise QueueFull(self.get_msg("voicelinkQueueFull").format(self._size))
if not self._duplicate_track:
if item.uri in [track.uri for track in self._queue]:
raise DuplicateTrack(self.get_msg("voicelinkDuplicateTrack"))
tracks = self.tracks(incTrack=True)
lastIndex = len(tracks)
for track in reversed(tracks):
@@ -214,5 +214,6 @@ class FairQueue(Queue):
break
lastIndex += 1
self._set.add(track.requester)
await self.put_at_index(lastIndex, item)
self.put_at_index(lastIndex, item)
return lastIndex

View File

@@ -2,7 +2,7 @@ import json, function as func
from discord import Member, VoiceChannel
from discord.ext import commands
from voicelink import Player, Track, Playlist, NodePool, connect_channel, decode
from voicelink import Player, Track, Playlist, NodePool, connect_channel, decode, LoopType
class TempCtx():
def __init__(self, author: Member, channel: VoiceChannel) -> None:
@@ -76,8 +76,8 @@ async def skipTo(player: Player, member: Member, data: dict):
if index > 1:
player.queue.skipto(index)
if player.queue._repeat == 1:
await player.set_repeat("off")
if player.queue._repeat.mode == LoopType.track:
await player.set_repeat(LoopType.off.name)
await player.stop()
async def backTo(player: Player, member: Member, data: dict):