Merge branch 'beta' into main

This commit is contained in:
MiguVT
2025-08-05 17:02:58 +02:00
committed by GitHub
29 changed files with 600 additions and 393 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -2,9 +2,13 @@ name: Docker Image CI
on:
push:
branches: [ "main" ]
tags:
- '*' # Trigger on any tag
branches:
- main # Trigger on push to main branch
- beta # Trigger on push to beta branch
paths-ignore:
- '**.md'
- '**.md'
permissions:
packages: write
@@ -14,16 +18,36 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build the Docker image
run: docker build . --file Dockerfile --tag vocard:latest
- name: Log in to GitHub Docker Registry
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- uses: actions/checkout@v4
- name: Push the Docker image
run: |
docker tag vocard:latest ghcr.io/chocomeow/vocard:latest # Ensure lowercase
docker push ghcr.io/chocomeow/vocard:latest # Ensure lowercase
- name: Determine Image Tag
id: image_tag
run: |
if [ "${{ github.ref_type }}" == "tag" ]; then
IMAGE_NAME="ghcr.io/chocomeow/vocard:${{ github.ref_name }}"
elif [ "${{ github.ref }}" == "refs/heads/beta" ]; then
IMAGE_NAME="ghcr.io/chocomeow/vocard:beta"
elif [ "${{ github.ref }}" == "refs/heads/main" ]; then
IMAGE_NAME="ghcr.io/chocomeow/vocard:latest"
else
echo "Error: Triggered on an unexpected ref: ${{ github.ref }}"
exit 1
fi
echo "IMAGE_NAME=${IMAGE_NAME}" >> $GITHUB_ENV
echo "Set image name to: ${IMAGE_NAME}"
- name: Build the Docker image
run: |
echo "Building image: ${{ env.IMAGE_NAME }}"
docker build . --file Dockerfile --tag ${{ env.IMAGE_NAME }}
- name: Log in to GitHub Docker Registry
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Push the Docker image
run: |
echo "Pushing image: ${{ env.IMAGE_NAME }}"
docker push ${{ env.IMAGE_NAME }}
echo "Successfully pushed ${{ env.IMAGE_NAME }}"

View File

@@ -29,7 +29,7 @@ Vocard is a highly customizable Discord music bot, designed to deliver a user-fr
* [Lavalink Server (Requires 4.0.0+)](https://github.com/freyacodes/Lavalink)
## Setup
Please see the [Setup Page](https://docs.vocard.xyz) in the docs to run this bot yourself!
Please see the [Setup Page](https://docs.vocard.xyz/latest/bot/setup) in the docs to run this bot yourself!
## Need Help?
Join the [Vocard Support Discord](https://discord.gg/wRCgB7vBQv) for help or questions.

View File

@@ -21,9 +21,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import aiohttp, random, bs4, re
import aiohttp
import random
import re
import hmac
import hashlib
import base64
import json
import urllib.parse
import function as func
from datetime import datetime
from abc import ABC, abstractmethod
from urllib.parse import quote
from math import floor
@@ -71,9 +79,6 @@ Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/530.9 (KHTM
Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/530.6 (KHTML, like Gecko) Chrome/ Safari/530.6
Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/ Safari/530.5'''
LYRIST_ENDPOINT = "https://lyrist.vercel.app/api/"
LRCLIB_ENDPOINT = "https://lrclib.net/api/"
class LyricsPlatform(ABC):
@abstractmethod
async def get_lyrics(self, title: str, artist: str) -> Optional[dict[str, str]]:
@@ -207,9 +212,12 @@ class Genius(LyricsPlatform):
return {"default": song.lyrics}
class Lyrist(LyricsPlatform):
def __init__(self):
self.base_url: str = "https://lyrist.vercel.app/api/"
async def get_lyrics(self, title: str, artist: str) -> Optional[dict[str, str]]:
try:
request_url = LYRIST_ENDPOINT + title + "/" + artist
request_url = self.base_url + title + "/" + artist
async with aiohttp.ClientSession() as session:
resp = await session.get(url=request_url, headers={'User-Agent': random.choice(userAgents)})
if resp.status != 200:
@@ -221,6 +229,9 @@ class Lyrist(LyricsPlatform):
return None
class Lrclib(LyricsPlatform):
def __init__(self):
self.base_url: str = "https://lrclib.net/api/"
async def get(self, url, params: dict = None) -> list[dict]:
try:
async with aiohttp.ClientSession() as session:
@@ -231,15 +242,135 @@ class Lrclib(LyricsPlatform):
except:
return []
async def get_lyrics(self, title, artist):
async def get_lyrics(self, title: str, artist: str) -> Optional[dict[str, str]]:
params = {"q": title}
result = await self.get(LRCLIB_ENDPOINT + "search", params)
result = await self.get(self.base_url + "search", params)
if result:
return {"default": result[0].get("plainLyrics", "")}
"""
Strvm/musicxmatch-api: a reverse engineered API wrapper for MusicXMatch
Copyright (c) 2025 Strvm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
class MusixMatch(LyricsPlatform):
def __init__(self):
self.base_url = "https://www.musixmatch.com/ws/1.1/"
self.headers = {'User-Agent': random.choice(userAgents)}
self.secret: Optional[str] = None
async def search_tracks(self, track_query: str, page: int = 1) -> dict:
url = f"track.search?app_id=web-desktop-app-v1.0&format=json&q={urllib.parse.quote(track_query)}&f_has_lyrics=true&page_size=5&page={page}"
return await self.make_request(url)
async def get_track_lyrics(self, track_id: Optional[str] = None, track_isrc: Optional[str] = None) -> dict:
if not (track_id or track_isrc):
raise ValueError("Either track_id or track_isrc must be provided.")
param = f"track_id={track_id}" if track_id else f"track_isrc={track_isrc}"
url = f"track.lyrics.get?app_id=web-desktop-app-v1.0&format=json&{param}"
return await self.make_request(url)
async def get_latest_app(self):
url = "https://www.musixmatch.com/search"
async with aiohttp.ClientSession() as session:
async with session.get(url, headers={**self.headers, "Cookie": "mxm_bab=AB"}) as response:
html_content = await response.text()
pattern = r'src="([^"]*/_next/static/chunks/pages/_app-[^"]+\.js)"'
matches = re.findall(pattern, html_content)
if not matches:
raise Exception("_app URL not found in the HTML content.")
return matches[-1]
async def get_secret(self) -> str:
async with aiohttp.ClientSession() as session:
async with session.get(await self.get_latest_app(), headers=self.headers, timeout=5) as response:
javascript_code = await response.text()
pattern = r'from\(\s*"(.*?)"\s*\.split'
match = re.search(pattern, javascript_code)
if match:
encoded_string = match.group(1)
reversed_string = encoded_string[::-1]
decoded_bytes = base64.b64decode(reversed_string)
return decoded_bytes.decode("utf-8")
else:
raise Exception("Encoded string not found in the JavaScript code.")
async def generate_signature(self, url: str) -> str:
current_date = datetime.now()
date_str = f"{current_date.year}{str(current_date.month).zfill(2)}{str(current_date.day).zfill(2)}"
message = (url + date_str).encode()
if not self.secret:
self.secret = await self.get_secret()
key = self.secret.encode()
hash_output = hmac.new(key, message, hashlib.sha256).digest()
signature = (
"&signature="
+ urllib.parse.quote(base64.b64encode(hash_output).decode())
+ "&signature_protocol=sha256"
)
return signature
async def make_request(self, url: str) -> dict:
url = url.replace("%20", "+").replace(" ", "+")
url = self.base_url + url
signed_url = url + await self.generate_signature(url)
async with aiohttp.ClientSession() as session:
async with session.get(signed_url, headers=self.headers, timeout=5) as response:
if response.status != 200:
raise Exception(f"HTTP Error: {response.status} for URL: {signed_url}")
try:
text_data = await response.text()
return json.loads(text_data)
except json.JSONDecodeError:
raise Exception(f"Failed to parse JSON. Response: {text_data}")
async def get_lyrics(self, title: str, artist: str) -> Optional[dict[str, str]]:
results = await self.search_tracks(track_query=f"{artist} {title}" if artist else title)
track_list = results.get("message", {}).get("body", {}).get("track_list")
if not track_list:
return None
track_id = track_list[0]["track"]["track_id"]
lyric = await self.get_track_lyrics(track_id=track_id)
lyrics_body = lyric.get("message", {}).get("body", {}).get("lyrics", {}).get("lyrics_body", "")
if not lyrics_body:
return None
return {"default": lyrics_body}
LYRICS_PLATFORMS: dict[str, Type[LyricsPlatform]] = {
"a_zlyrics": A_ZLyrics,
"genius": Genius,
"lyrist": Lyrist,
"lrclib": Lrclib
"lrclib": Lrclib,
"musixmatch": MusixMatch
}

View File

@@ -93,7 +93,7 @@ class Basic(commands.Cog):
node = voicelink.NodePool.get_node()
if not node:
return []
tracks: list[voicelink.Track] = await node.get_tracks(current, requester=interaction.user, search_type=SearchType.SPOTIFY)
tracks: list[voicelink.Track] = await node.get_tracks(current, requester=interaction.user)
return [app_commands.Choice(name=truncate_string(f"🎵 {track.author} - {track.title}", 100), value=truncate_string(f"{track.author} - {track.title}", 100)) for track in tracks] if tracks else []
history = {track["identifier"]: track for track_id in reversed(await get_user(interaction.user.id, "history")) if (track := voicelink.decode(track_id))["uri"]}

View File

@@ -99,16 +99,16 @@ class Settings(commands.Cog, name="settings"):
@settings.command(name="queue", aliases=get_aliases("queue"))
@app_commands.choices(mode=[
app_commands.Choice(name="FairQueue", value="FairQueue"),
app_commands.Choice(name="Queue", value="Queue")
app_commands.Choice(name=queue_type.capitalize(), value=queue_type)
for queue_type in voicelink.queue.QUEUE_TYPES.keys()
])
@commands.has_permissions(manage_guild=True)
@commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild)
async def queue(self, ctx: commands.Context, mode: str):
"Change to another type of queue mode."
mode = "FairQueue" if mode.lower() == "fairqueue" else "Queue"
await update_settings(ctx.guild.id, {"$set": {"queueType": mode}})
await send(ctx, "setqueue", mode)
mode = mode if mode.lower() in voicelink.queue.QUEUE_TYPES else next(iter(voicelink.queue.QUEUE_TYPES))
await update_settings(ctx.guild.id, {"$set": {"queue_type": mode}})
await send(ctx, "setQueue", mode.capitalize())
@settings.command(name="247", aliases=get_aliases("247"))
@commands.has_permissions(manage_guild=True)
@@ -126,8 +126,8 @@ class Settings(commands.Cog, name="settings"):
async def bypassvote(self, ctx: commands.Context):
"Toggles voting system."
settings = await get_settings(ctx.guild.id)
toggle = settings.get('votedisable', True)
await update_settings(ctx.guild.id, {"$set": {'votedisable': not toggle}})
toggle = settings.get('disabled_vote', True)
await update_settings(ctx.guild.id, {"$set": {'disabled_vote': not toggle}})
await send(ctx, 'bypassVote', await get_lang(ctx.guild.id, "enabled" if not toggle else "disabled"))
@settings.command(name="view", aliases=get_aliases("view"))
@@ -149,16 +149,16 @@ class Settings(commands.Cog, name="settings"):
settings.get('lang', 'EN'),
settings.get('controller', True),
dj_role.name if dj_role else 'None',
settings.get('votedisable', False),
settings.get('disabled_vote', False),
settings.get('24/7', False),
settings.get('volume', 100),
ctime(settings.get('playTime', 0) * 60 * 1000),
ctime(settings.get('played_time', 0) * 60 * 1000),
inline=True)
)
embed.add_field(name=texts[3], value=texts[4].format(
settings.get("queueType", "Queue"),
settings.get("queue_type", "Queue"),
func.settings.max_queue,
settings.get("duplicateTrack", True)
settings.get("duplicate_track", True)
))
if stage_template := settings.get("stage_announce_template"):
@@ -212,12 +212,12 @@ class Settings(commands.Cog, name="settings"):
async def duplicatetrack(self, ctx: commands.Context):
"Toggle Vocard to prevent duplicate songs from queuing."
settings = await get_settings(ctx.guild.id)
toggle = not settings.get('duplicateTrack', False)
toggle = not settings.get('duplicate_track', False)
player: voicelink.Player = ctx.guild.voice_client
if player:
player.queue._allow_duplicate = toggle
await update_settings(ctx.guild.id, {"$set": {'duplicateTrack': toggle}})
await update_settings(ctx.guild.id, {"$set": {'duplicate_track': toggle}})
return await send(ctx, "toggleDuplicateTrack", await get_lang(ctx.guild.id, "disabled" if toggle else "enabled"))
@settings.command(name="customcontroller", aliases=get_aliases("customcontroller"))
@@ -241,6 +241,17 @@ class Settings(commands.Cog, name="settings"):
await update_settings(ctx.guild.id, {"$set": {'controller_msg': toggle}})
await send(ctx, 'toggleControllerMsg', await get_lang(ctx.guild.id, "enabled" if toggle else "disabled"))
@settings.command(name="silentmsg", aliases=get_aliases("silentmsg"))
@commands.has_permissions(manage_guild=True)
@commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild)
async def silentmsg(self, ctx: commands.Context):
"Toggle silent messaging to send discreet messages without alerting recipients."
settings = await get_settings(ctx.guild.id)
toggle = not settings.get('silent_msg', False)
await update_settings(ctx.guild.id, {"$set": {'silent_msg': toggle}})
await send(ctx, 'toggleSilentMsg', await get_lang(ctx.guild.id, "enabled" if toggle else "disabled"))
@settings.command(name="stageannounce", aliases=get_aliases("stageannounce"))
@commands.has_permissions(manage_guild=True)

View File

@@ -101,7 +101,6 @@ services:
# - 8000:8000
# networks:
# - vocard
# - web
vocard:
container_name: vocard

View File

@@ -166,8 +166,8 @@ def get_lang_non_async(guild_id: int, *keys) -> Union[list[str], str]:
LANGS[lang] = open_json(os.path.join("langs", f"{lang}.json"))
if len(keys) == 1:
return LANGS.get(lang, {}).get(keys[0], "Language pack not found!")
return [LANGS.get(lang, {}).get(key, "Language pack not found!") for key in keys]
return LANGS.get(lang, {}).get(keys[0], "Not found!")
return [LANGS.get(lang, {}).get(key, "Not found!") for key in keys]
def format_bytes(bytes: int, unit: bool = False):
if bytes <= 1_000_000_000:
@@ -212,19 +212,30 @@ async def send(
# Determine the sending function
send_func = (
ctx.send if isinstance(ctx, commands.Context) else
ctx.response.send_message if not ctx.response.is_done() else
ctx.followup.send
ctx.followup.send if ctx.response.is_done() else
ctx.response.send_message
)
# Check settings for delete_after duration
settings = await get_settings(ctx.guild.id)
if settings and ctx.channel.id == settings.get("music_request_channel", {}).get("text_channel_id"):
delete_after = 10
send_kwargs = {
"content": text,
"embed": embed,
"ephemeral": ephemeral,
"allowed_mentions": ALLOWED_MENTIONS,
"silent": settings.get("silent_msg", False),
}
if "delete_after" in send_func.__code__.co_varnames:
if settings and ctx.channel.id == settings.get("music_request_channel", {}).get("text_channel_id"):
delete_after = 10
send_kwargs["delete_after"] = delete_after
if view:
send_kwargs["view"] = view
# Send the message or embed
if view:
return await send_func(text, embed=embed, view=view, delete_after=delete_after, ephemeral=ephemeral, allowed_mentions=ALLOWED_MENTIONS)
return await send_func(text, embed=embed, delete_after=delete_after, ephemeral=ephemeral, allowed_mentions=ALLOWED_MENTIONS)
return await send_func(**send_kwargs)
async def update_db(db: AsyncIOMotorCollection, tempStore: dict, filter: dict, data: dict) -> bool:
for mode, action in data.items():

View File

@@ -12,12 +12,14 @@ RATELIMIT_COUNTER: Dict[int, Dict[str, float]] = {}
SCOPES = {
"prefix": str,
"lang": str,
"queueType": str,
"queue_type": str,
"dj": int,
"controller": bool,
"controller_msg": bool,
"24/7": bool,
"votedisable": bool,
"duplicateTrack": bool,
"disabled_vote": bool,
"duplicate_track": bool,
"silent_msg": bool,
"default_controller": dict,
"stage_announce_template": str
}
@@ -314,7 +316,7 @@ async def toggleAutoplay(player: Player, member: Member, data: Dict) -> Dict:
return {
"op": "toggleAutoplay",
"status": check,
"guildId": player.guild.id,
"guildId": str(player.guild.id),
"requesterId": str(member.id)
}

View File

@@ -2,7 +2,6 @@
"unknownException": "⚠️ 執行命令時出現問題!請稍後再試,或加入我們的 Discord 伺服器獲得進一步支援。",
"enabled": "已啟用",
"disabled": "已停用",
"nodeReconnect": "請稍後再試!在節點重新連接後再嘗試。",
"noChannel": "沒有語音頻道可供連接。請提供一個語音頻道或加入一個語音頻道。",
"alreadyConnected": "已經連接到語音頻道。",
@@ -16,13 +15,14 @@
"changedLanguage": "已成功切換到 `{0}` 語言包。",
"setPrefix": "完成!我的前綴在您的伺服器中現在是 `{0}`。嘗試運行 `{1}ping` 來測試它。",
"setDJ": "已將 DJ 設置為 {0}。",
"setqueue": "已將隊列模式設置為 `{0}`。",
"setQueue": "已將隊列模式設置為 `{0}`。",
"247": "現在您有 `{0}` 24/7 模式。",
"bypassVote": "現在您有 `{0}` 投票系統。",
"setVolume": "已將音量設置為 `{0}`%",
"togglecontroller": "現在您已 `{0}` 音樂控制器。",
"toggleDuplicateTrack": "現在您已 `{0}` 防止隊列中存在重複曲目。",
"toggleControllerMsg": "現在您已從音樂控制器 `{0}` 消息。",
"toggleSilentMsg": "你{0}靜默消息。",
"settingsMenu": "伺服器設置 | {0}",
"settingsTitle": "❤️ 基本資訊:",
"settingsValue": "```前綴:{0}\n語言{1}\n音樂控制器{2}\nDJ 角色:@{3}\n投票繞過{4}\n24/7{5}\n默認音量{6}%\n播放時間{7}```",
@@ -38,7 +38,6 @@
"addEffect": "套用音效`{0}`濾鏡。",
"clearEffect": "聲音效果已清除!",
"FilterTagAlreadyInUse": "此聲音效果已在使用中!請使用 /cleareffect <Tag> 移除它。",
"playlistViewTitle": "📜 所有 {0} 的播放清單",
"playlistViewHeaders": "ID:,時間:,名稱:,曲目數:",
"playlistFooter": "輸入 /playlist play [播放清單] 加入此播放清單至隊列中。",
@@ -77,7 +76,6 @@
"inboxFull": "抱歉!{0} 的收件匣已滿。",
"inboxNoMsg": "您的收件匣中沒有任何訊息。",
"invitationSent": "已發送邀請給 {0}。",
"notInChannel": "{0},您必須在 {1} 中使用語音指令。如果您已在語音頻道中,請重新加入!",
"noTrackPlaying": "現在沒有歌曲正在播放",
"noTrackFound": "找不到符合該查詢的歌曲!請提供有效的網址。",
@@ -92,16 +90,13 @@
"lyricsNotFound": "找不到歌詞。輸入 /lyrics <歌曲名稱> <作者> 查找歌詞。",
"missingTrackInfo": "有些音軌資訊缺失。",
"noVoiceChannel": "找不到語音頻道!",
"playlistAddError": "您無權將串流視訊添加到播放清單中!",
"playlistAddError2": "添加音軌到播放清單時發生問題!",
"playlistlimited": "您已達到上限!您只能將 {0} 首歌曲添加到播放清單中。",
"playlistrepeated": "您的播放清單中已經存在相同的音軌!",
"playlistAdded": "❤️ 已將 **{0}** 添加到 {1} 的播放清單中 [`{2}`]!",
"playerDropdown": "選擇要跳轉到的音軌...",
"playerFilter": "選擇要套用的篩選器...",
"buttonBack": "返回",
"buttonPause": "暫停",
"buttonResume": "繼續",
@@ -117,30 +112,24 @@
"buttonForward": "前進",
"buttonRewind": "後退",
"buttonLyrics": "歌詞",
"nowplayingDesc": "**現在播放:**\n```{0}```",
"nowplayingField": "接下來播放:",
"nowplayingLink": "在 {0} 上收聽",
"connect": "已連接至 {0}",
"live": "直播",
"playlistLoad": " 🎶 已添加播放清單 **{0}**,共 `{1}` 首歌曲至隊列。",
"trackLoad": "已添加 **[{0}](<{1}>)**,由 **{2}** (`{3}`) 開始播放。\n",
"trackLoad_pos": "已將 **[{0}](<{1}>)**,由 **{2}** (`{3}`) 添加到隊列中位置 **{4}**\n",
"searchTitle": "搜索查詢: {0}",
"searchDesc": "➥ 平台: {0} **{1}**\n➥ 結果: **{2}**\n\n{3}",
"searchWait": "選擇您想要添加到隊列中的歌曲。",
"searchTimeout": "搜索超時。請稍後再試。",
"searchSuccess": "已將歌曲添加到隊列中。",
"queueTitle": "即將播放的隊列:",
"historyTitle": "歷史隊列:",
"viewTitle": "音樂隊列",
"viewDesc": "**現正播放:[點擊我]({0}) ⮯**\n{1}",
"viewFooter": "頁數:{0}/{1} | 總長度:{2}",
"pauseError": "播放器已經暫停。",
"pauseVote": "{0} 已投票暫停歌曲。[{1}/{2}]",
"paused": "播放器已被 `{0}` 暫停。",
@@ -153,13 +142,10 @@
"skipError": "沒有歌曲可以跳過。",
"skipVote": "{0} 已投票跳過歌曲。[{1}/{2}]",
"skipped": "播放器已被 `{0}` 跳過歌曲。",
"backVote": "{0} 已投票跳到上一首歌曲。[{1}/{2}]",
"backed": "播放器已被 `{0}` 跳到上一首歌曲。",
"leaveVote": "{0} 已投票停止播放器。[{1}/{2}]",
"left": "播放器已被 `{0}` 停止。",
"seek": "將播放器設置到 **{0}**。",
"repeat": "重複模式已設置為 `{0}`。",
"cleared": "清除了 `{0}` 中的所有歌曲。",
@@ -170,25 +156,20 @@
"swapped": "已交換 `{0}` 和 `{1}`。",
"moved": "已將 `{0}` 移動到 `{1}`。",
"autoplay": "自動播放模式現在為 **{0}**。",
"notdj": "您不是DJ當前DJ為 {0}。",
"djToMe": "您無法將DJ權限轉移給自己或機器人。",
"djnotinchannel": "`{0}` 不在語音頻道中。",
"djswap": "您已將DJ權限轉移給 `{0}`。",
"chaptersDropdown": "選擇要跳轉到的章節...",
"noChaptersFound": "找不到任何章節!",
"chatpersNotSupport": "此命令僅支持 YouTube 影片!",
"voicelinkQueueFull": "抱歉,您已達到隊列中 `{0}` 首歌曲的最大數量!",
"voicelinkOutofList": "請提供有效的歌曲索引!",
"voicelinkDuplicateTrack": "抱歉,此歌曲已在隊列中。",
"deocdeError": "解碼文件時出現問題!",
"invalidStartTime": "無效的開始時間! 時間必須在 `00:00` 和 `{0}` 之間。",
"invalidEndTime": "無效的結束時間! 時間必須在 `00:00` 和 `{0}` 之間。",
"invalidTimeOrder": "結束時間不能小於或等於開始時間。",
"setStageAnnounceTemplate": "完成!從現在開始,像您現在的語音狀態將根據您的模板命名。您應該在幾秒鐘內看到它更新。",
"createSongRequestChannel": "一個歌曲請求頻道 ({0}) 已建立!您可以在該頻道中透過歌曲名稱或 URL 開始要求任何歌曲,而無需使用機器人前綴。"
}

View File

@@ -2,7 +2,6 @@
"unknownException": "⚠️ Beim Ausführen des Befehls ist etwas schiefgelaufen! Bitte versuche es später erneut oder tritt unserem Discord-Server bei, um weiteren Support zu erhalten.",
"enabled": "aktiviert",
"disabled": "deaktiviert",
"nodeReconnect": "Bitte versuche es erneut, nachdem sich die Node wieder verbunden hat.",
"noChannel": "Kein Sprachkanal zum Verbinden gefunden. Bitte stelle entweder einen zur Verfügung oder trete einem bei.",
"alreadyConnected": "Bereits mit einem Sprachkanal verbunden.",
@@ -16,13 +15,14 @@
"changedLanguage": "Erfolgreich auf das Sprachpaket `{0}` geändert.",
"setPrefix": "Erledigt! Mein Präfix ist jetzt `{0}` auf deinem Server. Versuche, `{1}ping` auszuführen, um es zu testen.",
"setDJ": "Stelle den DJ auf {0}.",
"setqueue": "Stelle den Warteschlangenmodus auf `{0}` ein.",
"setQueue": "Stelle den Warteschlangenmodus auf `{0}` ein.",
"247": "Der 24/7-Modus wurde erfolgreich `{0}`.",
"bypassVote": "Das Abstimmungssystem wurde `{0}`",
"setVolume": "Stelle die Lautstärke auf `{0}`%.",
"togglecontroller": "Der Musikcontroller wurde erfolgreich `{0}`",
"toggleDuplicateTrack": "Du hast das hinzufügen von doppelten Tracks in der Warteschlange `{0}`",
"toggleControllerMsg": "Nachrichten vom Musik-Controller wurden erfolgreich `{0}`",
"toggleSilentMsg": "Sie haben {0} stille Nachrichten.",
"settingsMenu": "Servereinstellungen | {0}",
"settingsTitle": "❤️ Grundlegende Informationen:",
"settingsValue": "```Präfix: {0}`\nSprache: {1}\nMusik-Controller: {2}\nDJ-Rolle: @{3}\nAbstimmungsumgehung: {4}\n24/7 Play: {5}\nStandard Lautstärke: {6}%\nSpielzeit: {7}```",
@@ -38,7 +38,6 @@
"addEffect": "Wende den Effekt `{0}` Filter an.",
"clearEffect": "Die Soundeffekte wurden gelöscht!",
"FilterTagAlreadyInUse": "Diese Soundeffekte sind bereits im Einsatz! Bitte verwende /cleareffect <Tag>, um sie zu entfernen.",
"playlistViewTitle": "📜 Alle Playlists von {0}",
"playlistViewHeaders": "ID:,Zeit:,Name:,Tracks:",
"playlistFooter": "Gebe /playlist play [playlist] ein, um die Playlist in die Warteschlange einzufügen.",
@@ -77,7 +76,6 @@
"inboxFull": "Es tut mir leid! Der Posteingang von {0} ist voll.",
"inboxNoMsg": "Es sind keine Nachrichten in Deinem Posteingang.",
"invitationSent": "Einladung an {0} gesendet.",
"notInChannel": "{0}, Du musst in {1} sein, um Sprachbefehle nutzen zu können. Bitte betrete den Sprachkanal erneut bei, wenn Du dich im Voice Channel befindest!",
"noTrackPlaying": "Es werden derzeit keine Songs abgespielt",
"noTrackFound": "Es wurden keine Songs mit dieser Abfrage gefunden! Bitte gib eine gültige URL an.",
@@ -92,16 +90,13 @@
"lyricsNotFound": "Es wurden keine Songtexte gefunden. Gebe /lyrics <Song Name> <Autor> ein, um die Songtexte zu finden.",
"missingTrackInfo": "Einige Track-Informationen fehlen.",
"noVoiceChannel": "Dieser Sprachkanal wurde nicht gefunden!",
"playlistAddError": "Du darfst Deiner Wiedergabenliste keine aktiven Streams hinzufügen!",
"playlistAddError2": "Es gab ein Problem beim Hinzufügen von Tracks zur Wiedergabenliste!",
"playlistlimited": "Du hast das Limit erreicht! Du kannst nur noch {0} Songs zu Deiner Wiedergabenliste hinzufügen.",
"playlistrepeated": "In Deiner Wiedergabenliste gibt es bereits den gleichen Track!",
"playlistAdded": "❤️ **{0}** wurde in der Wiedergabenliste [`{2}`] von {1} hinzugefügt.",
"playerDropdown": "Wähle einen Song aus, um zu überspringen ...",
"playerFilter": "Wähle einen Filter aus, um ihn anzuwenden ...",
"buttonBack": "Zurück",
"buttonPause": "Pause",
"buttonResume": "Weiter",
@@ -117,30 +112,24 @@
"buttonForward": "Vorspulen",
"buttonRewind": "Zurückspulen",
"buttonLyrics": "Liedtexte",
"nowplayingDesc": "**Jetzt wird abgespielt:**\n```{0}```",
"nowplayingField": "Als nächstes:",
"nowplayingLink": "Auf {0} anhören",
"connect": "Verbunden mit {0}",
"live": "LIVE",
"playlistLoad": " 🎶 Die Wiedergabeliste **{0}** mit `{1}` Songs wurde zur Warteschlange hinzugefügt.",
"trackLoad": "**[{0}](<{1}>)** von **{2}** (`{3}`) wurde zum Abspielen hinzugefügt.\n",
"trackLoad_pos": "**[{0}](<{1}>)** von **{2}** (`{3}`) wurde in der Warteschlange zu Position **{4}** hinzugefügt.\n",
"searchTitle": "Suchabfrage: {0}",
"searchDesc": "➥ Plattform: {0} **{1}**\n➥ Ergebnisse: **{2}**\n\n{3}",
"searchWait": "Wähle den Song aus, den Du zur Warteschlange hinzufügen möchtest.",
"searchTimeout": "Die Suche wurde abgebrochen. Bitte versuche es später erneut.",
"searchSuccess": "Der Song wurde zur Warteschlange hinzugefügt.",
"queueTitle": "Kommende Warteschlange:",
"historyTitle": "Vorherige Warteschlange:",
"viewTitle": "Musik-Warteschlange",
"viewDesc": "**Jetzt wird abgespielt: [Hier klicken]({0}) ⮯**\n{1}",
"viewFooter": "Seite: {0}/{1} | Gesamtdauer: {2}",
"pauseError": "Der Player ist bereits pausiert.",
"pauseVote": "{0} hat für eine Pause des Songs abgestimmt. [{1}/{2}]",
"paused": "`{0}` hat den Player pausiert.",
@@ -153,13 +142,10 @@
"skipError": "Es gibt keine Songs, die übersprungen werden können.",
"skipVote": "{0} hat für das Überspringen des Songs abgestimmt. [{1}/{2}]",
"skipped": "`{0}` hat den Song übersprungen.",
"backVote": "{0} hat für das Überspringen zum vorherigen Song gestimmt. [{1}/{2}]",
"backed": "{0} hat zum vorherigen Song übersprungen.",
"leaveVote": "{0} hat für das Anhalten des Players gestimmt. [{1}/{2}]",
"left": "`{0}` hat den Player angehalten.",
"seek": "Der Player wurde auf **{0}** gesetzt.",
"repeat": "Der Wiederholungsmodus wurde auf `{0}` gesetzt",
"cleared": "Alle Tracks in `{0}` wurden gelöscht",
@@ -170,25 +156,20 @@
"swapped": "`{0}` und `{1}` wurden ausgetauscht.",
"moved": "`{0}` zu `{1}` verschoben",
"autoplay": "Der Autoplay-Modus ist jetzt **{0}**",
"notdj": "Du bist kein DJ, der aktuelle DJ ist {0}.",
"djToMe": "Du kannst den DJ nicht an dich selbst oder einer App übertragen.",
"djnotinchannel": "`{0}` ist nicht im Sprachkanal.",
"djswap": "Du hast die DJ-Rolle auf `{0}` übertragen.",
"chaptersDropdown": "Wähle ein Kapitel zum Überspringen aus...",
"noChaptersFound": "Es wurden keine Kapitel gefunden!",
"chatpersNotSupport": "Dieser Befehl unterstützt nur YouTube-Videos!",
"voicelinkQueueFull": "Entschuldigung, Du hast das Maximum von `{0}` Tracks in der Warteschlange erreicht.",
"voicelinkOutofList": "Bitte gib einen gültigen Track-Index an!",
"voicelinkDuplicateTrack": "Entschuldigung, dieser Track ist bereits in der Warteschlange.",
"deocdeError": "Beim Dekodieren der Datei ist etwas schief gelaufen!",
"invalidStartTime": "Ungültige Startzeit! Die Zeit muss innerhalb von `00:00` und `{0}` liegen.",
"invalidEndTime": "Ungültige Endzeit! Die Zeit muss innerhalb von `00:00` und `{0}` liegen.",
"invalidTimeOrder": "Die Endzeit darf nicht kleiner oder gleich dem Startzeit sein.",
"setStageAnnounceTemplate": "Fertig! Ab sofort wird der Sprachstatus wie der, in dem Du Dich gerade befindest, gemäß Deiner Vorlage benannt. Du solltest in wenigen Sekunden eine Aktualisierung sehen.",
"createSongRequestChannel": "Der Song Request Channel ({0}) wurde erstellt! Du kannst jeden Song nach Namen oder URL in diesem Kanal anfordern, ohne den Bot-Präfix verwenden zu müssen."
}

View File

@@ -2,7 +2,6 @@
"unknownException": "⚠️ Something wrong while running the command! Please try again later or join our discord server for further support.",
"enabled": "enabled",
"disabled": "disabled",
"nodeReconnect": "Please try again! After the node reconnects.",
"noChannel": "No voice channel to connect. Please either provide one or join one.",
"alreadyConnected": "Already connected to a voice channel.",
@@ -16,13 +15,14 @@
"changedLanguage": "Successfully changed to `{0}` language pack.",
"setPrefix": "Done! My prefix in your server is now `{0}`. Try running `{1}ping` to test it out.",
"setDJ": "Set the dj to {0}.",
"setqueue": "Set queue mode to `{0}`.",
"setQueue": "Set queue mode to `{0}`.",
"247": "24/7 mode is now `{0}`.",
"bypassVote": "You have `{0}` voting system.",
"setVolume": "Set the volume to `{0}`%",
"togglecontroller": "You now have `{0}` the music controller.",
"toggleDuplicateTrack": "You have now `{0}` duplicate track prevention.",
"toggleControllerMsg": "You now have `{0}` messages from the music controller.",
"toggleSilentMsg": "You have {0} silent messaging.",
"settingsMenu": "Server Settings | {0}",
"settingsTitle": "❤️ Basic Info:",
"settingsValue": "```Prefix: {0}\nLanguage: {1}\nMusic Controller: {2}\nDJ Role: @{3}\nVote Bypass: {4}\n24/7: {5}\nDefault Volume: {6}%\nPlay Time: {7}```",
@@ -38,7 +38,6 @@
"addEffect": "Applied the `{0}` effect.",
"clearEffect": "The sound effects have been cleared!",
"FilterTagAlreadyInUse": "This sound effect is already in use! Please use /cleareffect <Tag> to remove it.",
"playlistViewTitle": "📜 All of {0}'s Playlists",
"playlistViewHeaders": "ID:,Time:,Name:,Tracks:",
"playlistFooter": "Type /playlist play [playlist] to add a playlist the into the queue.",
@@ -77,7 +76,6 @@
"inboxFull": "Sorry! {0}'s inbox is full.",
"inboxNoMsg": "There are no messages in your inbox.",
"invitationSent": "Invitation sent to {0}.",
"notInChannel": "{0}, you must be in {1} to use voice commands. Please rejoin if you are in voice!",
"noTrackPlaying": "There are no songs playing right now",
"noTrackFound": "No songs were found with that query! Please provide a valid url.",
@@ -92,16 +90,13 @@
"lyricsNotFound": "Lyrics not found. Type /lyrics <Song Name> <Author> to find the lyrics.",
"missingTrackInfo": "Some track details are missing.",
"noVoiceChannel": "Voice Channel Not Found!",
"playlistAddError": "You are not allowed to add streaming videos to your playlist!",
"playlistAddError2": "There was a problem adding tracks to the playlist!",
"playlistlimited": "You have reached the limit! You can only add {0} songs to your playlist.",
"playlistrepeated": "This track is already in your playlist!",
"playlistAdded": "❤️ Added **{0}** into {1}'s playlist [`{2}`]!",
"playerDropdown": "Select a song to skip to ...",
"playerFilter": "Select a filter to apply ...",
"buttonBack": "Back",
"buttonPause": "Pause",
"buttonResume": "Resume",
@@ -117,30 +112,24 @@
"buttonForward": "Forward",
"buttonRewind": "Rewind",
"buttonLyrics": "Lyrics",
"nowplayingDesc": "**Now Playing:**\n```{0}```",
"nowplayingField": "Up Next:",
"nowplayingLink": "Listen on {0}",
"connect": "Connected to {0}",
"live": "LIVE",
"playlistLoad": " 🎶 Added the playlist **{0}** with `{1}` songs to the queue.",
"trackLoad": "Added **[{0}](<{1}>)** by **{2}** (`{3}`) to begin playing.\n",
"trackLoad_pos": "Added **[{0}](<{1}>)** by **{2}** (`{3}`) to the queue at position **{4}**\n",
"searchTitle": "Search Query: {0}",
"searchDesc": "➥ Platform: {0} **{1}**\n➥ Results: **{2}**\n\n{3}",
"searchWait": "Select the song you want to add to the queue.",
"searchTimeout": "Search timed out. Please try again later.",
"searchSuccess": "Song added to the queue.",
"queueTitle": "Upcoming Queue:",
"historyTitle": "History Queue:",
"viewTitle": "Music Queue",
"viewDesc": "**Now Playing: [Click Me]({0}) ⮯**\n{1}",
"viewFooter": "Page: {0}/{1} | Total Duration: {2}",
"pauseError": "The player is already paused.",
"pauseVote": "{0} has voted to pause the song. [{1}/{2}]",
"paused": "`{0}` has paused the player.",
@@ -153,13 +142,10 @@
"skipError": "There are no songs too skip to.",
"skipVote": "{0} has voted to skip the song. [{1}/{2}]",
"skipped": "`{0}` has skipped the song.",
"backVote": "{0} has voted to skip to the previous song. [{1}/{2}]",
"backed": "`{0}` has skipping to previous song.",
"leaveVote": "{0} has voted to stop the player. [{1}/{2}]",
"left": "`{0}` has stopped the player.",
"seek": "Set the player to **{0}**",
"repeat": "The repeat mode has been set to `{0}`",
"cleared": "Cleared all tracks in the `{0}`",
@@ -170,25 +156,20 @@
"swapped": "`{0}` and `{1}` have been swapped",
"moved": "Moved `{0}` to `{1}`",
"autoplay": "Autoplay mode is now **{0}**",
"notdj": "You are not DJ. The current DJ is {0}.",
"djToMe": "You cannot transfer role of DJ to yourself or a bot.",
"djnotinchannel": "`{0}` is not in the voice channel.",
"djswap": "You have transferred the role of dj to `{0}`.",
"chaptersDropdown": "Select a chapter to skip to ...",
"noChaptersFound": "No chapters has been found!",
"chatpersNotSupport": "This command only supports Youtube videos!",
"voicelinkQueueFull": "Sorry, you have reached the maximum of `{0}` tracks in the queue!",
"voicelinkOutofList": "Please provide a valid track index!",
"voicelinkDuplicateTrack": "Sorry, this track is already in the queue.",
"deocdeError": "Something went wrong while decoding the file!",
"invalidStartTime": "Invalid start time, it must be between `00:00` and `{0}`",
"invalidEndTime": "Invalid end time, it must be between `00:00` and `{0}`",
"invalidTimeOrder": "End time cannot be less than or equal to start time",
"setStageAnnounceTemplate": "Done! From now on, voice status like the one you're in now will be named according to your template. You should see it update in a few seconds.",
"createSongRequestChannel": "A song request channel ({0}) has been created! You can start requesting any song by name or URL in that channel, without needing to use the bot prefix."
}
}

View File

@@ -2,7 +2,6 @@
"unknownException": "⚠️ ¡Algo salió mal al ejecutar el comando! Por favor, intente de nuevo más tarde o únase a nuestro servidor de Discord para obtener más soporte.",
"enabled": "habilitado",
"disabled": "deshabilitado",
"nodeReconnect": "¡Por favor, inténtelo de nuevo! Después de que el nodo se reconecte.",
"noChannel": "No hay canal de voz al que conectarse. Por favor, proporcione uno o únase a uno.",
"alreadyConnected": "Ya conectado a un canal de voz.",
@@ -16,13 +15,14 @@
"changedLanguage": "Cambiado con éxito al paquete de idioma `{0}`.",
"setPrefix": "¡Listo! Mi prefijo en tu servidor ahora es `{0}`. Intenta ejecutar `{1}ping` para probarlo.",
"setDJ": "Establecer el DJ a {0}.",
"setqueue": "Establecer el modo de cola en `{0}`.",
"setQueue": "Establecer el modo de cola en `{0}`.",
"247": "Ahora tienes `{0}` modo 24/7.",
"bypassVote": "Ahora tienes `{0}` sistema de votación.",
"setVolume": "Ajustar el volumen a `{0}`%",
"togglecontroller": "Ahora tienes `{0}` el controlador de música.",
"toggleDuplicateTrack": "Ahora tienes `{0}` para evitar que se agreguen pistas duplicadas a la cola.",
"toggleControllerMsg": "Ahora tiene `{0}` mensajes del controlador de música.",
"toggleSilentMsg": "Tienes {0} mensajes silenciosos.",
"settingsMenu": "Configuración del servidor | {0}",
"settingsTitle": "❤️ Información básica:",
"settingsValue": "```Prefijo: {0}\nIdioma: {1}\nHabilitar controlador de música: {2}\nRol de DJ: @{3}\nBypass de votación: {4}\n24/7: {5}\nVolumen predeterminado: {6}%\nTiempo de reproducción: {7}```",
@@ -38,7 +38,6 @@
"addEffect": "plica el efecto `{0}` filtro.",
"clearEffect": "¡Los efectos de sonido se han borrado!",
"FilterTagAlreadyInUse": "¡Este efecto de sonido ya está en uso! Utilice /cleareffect <Tag> para eliminarlo.",
"playlistViewTitle": "📜 Todas las listas de reproducción de {0}",
"playlistViewHeaders": "ID:,Tiempo:,Nombre:,Pistas:",
"playlistFooter": "Escriba /playlist play [playlist] para agregar la lista de reproducción a la cola.",
@@ -77,7 +76,6 @@
"inboxFull": "¡Lo siento! La bandeja de entrada de {0} está llena.",
"inboxNoMsg": "No hay mensajes en su bandeja de entrada.",
"invitationSent": "Se ha enviado una invitación a {0}.",
"notInChannel": "{0}, debes estar en {1} para usar comandos de voz. ¡Vuelve a unirte si estás en voz!",
"noTrackPlaying": "Actualmente no hay canciones reproduciéndose.",
"noTrackFound": "No se encontraron canciones con esa consulta. Proporcione una URL válida.",
@@ -92,16 +90,13 @@
"lyricsNotFound": "No se encontraron letras. Escriba /lyrics <Nombre de la canción> <Autor> para buscar las letras.",
"missingTrackInfo": "Falta información de la canción.",
"noVoiceChannel": "¡Canal de voz no encontrado!",
"playlistAddError": "No está autorizado para agregar videos de transmisión a su lista de reproducción.",
"playlistAddError2": "Hubo un problema al agregar canciones a la lista de reproducción.",
"playlistlimited": "¡Ha alcanzado el límite! Solo puede agregar {0} canciones a su lista de reproducción.",
"playlistrepeated": "¡Ya hay una canción igual en su lista de reproducción!",
"playlistAdded": "❤️ Agregado **{0}** a la lista de reproducción de {1} [`{2}`]!",
"playerDropdown": "Seleccione una canción para saltar a ...",
"playerFilter": "Seleccione un filtro para aplicar ...",
"buttonBack": "Atrás",
"buttonPause": "Pausa",
"buttonResume": "Reanudar",
@@ -117,30 +112,24 @@
"buttonForward": "Adelante",
"buttonRewind": "Atrás",
"buttonLyrics": "Letras",
"nowplayingDesc": "**Reproduciendo ahora:**\n```{0}```",
"nowplayingField": "A continuación:",
"nowplayingLink": "Escucha en {0}",
"connect": "Conectado a {0}",
"live": "EN VIVO",
"playlistLoad": " 🎶 Se agregó la lista de reproducción {0} con {1} canciones a la cola.",
"trackLoad": "Se agregó **[{0}](<{1}>)** de {2} ({3}) para comenzar a reproducir.\n",
"trackLoad_pos": "Se agregó **[{0}](<{1}>)** de {2} ({3}) a la cola en la posición {4}\n",
"searchTitle": "Consulta de búsqueda: {0}",
"searchDesc": "➥ Plataforma: {0} **{1}**\n➥ Resultados: **{2}**\n\n{3}",
"searchWait": "Seleccione la canción que desea agregar a la cola.",
"searchTimeout": "La búsqueda ha expirado. Por favor, inténtelo de nuevo más tarde.",
"searchSuccess": "Canción agregada a la cola.",
"queueTitle": "Cola de próximas canciones:",
"historyTitle": "Cola de historial:",
"viewTitle": "Cola de música",
"viewDesc": "**Reproduciendo ahora: [Haga clic aquí]({0}) ⮯**\n{1}",
"viewFooter": "Página: {0}/{1} | Duración total: {2}",
"pauseError": "El reproductor ya está en pausa.",
"pauseVote": "{0} ha votado para pausar la canción. [{1}/{2}]",
"paused": "`{0}` ha pausado el reproductor.",
@@ -153,13 +142,10 @@
"skipError": "No hay canciones para saltar.",
"skipVote": "{0} ha votado para saltar la canción. [{1}/{2}]",
"skipped": "`{0}` ha saltado la canción.",
"backVote": "{0} ha votado para volver a la canción anterior. [{1}/{2}]",
"backed": "`{0}` ha vuelto a la canción anterior.",
"leaveVote": "{0} ha votado para detener el reproductor. [{1}/{2}]",
"left": "`{0}` ha detenido el reproductor.",
"seek": "El reproductor se estableció en **{0}**",
"repeat": "El modo de repetición se ha establecido en `{0}`",
"cleared": "Se borraron todas las canciones de `{0}`",
@@ -170,25 +156,20 @@
"swapped": "Se intercambiaron `{0}` y `{1}`",
"moved": "Se movió `{0}` a `{1}`",
"autoplay": "El modo de reproducción automática es ahora **{0}**",
"notdj": "No eres el DJ. El DJ actual es {0}.",
"djToMe": "No puedes transferir el rol de DJ a ti mismo o a un bot.",
"djnotinchannel": "`{0}` no está en el canal de voz.",
"djswap": "Has transferido el rol de DJ a `{0}`.",
"chaptersDropdown": "Selecciona un capítulo al que saltar ...",
"noChaptersFound": "¡No se encontraron capítulos!",
"chatpersNotSupport": "¡Este comando solo es compatible con videos de Youtube!",
"voicelinkQueueFull": "Lo siento, ¡ha alcanzado el máximo de `{0}` canciones en la cola!",
"voicelinkOutofList": "¡Proporcione un índice de pista válido!",
"voicelinkDuplicateTrack": "Lo siento, esta canción ya está en la cola.",
"deocdeError": "¡Algo salió mal al decodificar el archivo!",
"invalidStartTime": "Tiempo de inicio inválido! El tiempo debe estar entre `00:00` y `{0}`.",
"invalidEndTime": "Tiempo de finalización inválido! El tiempo debe estar entre `00:00` y `{0}`.",
"invalidTimeOrder": "El tiempo final no puede ser menor o igual que el tiempo de inicio.",
"setStageAnnounceTemplate": "¡Hecho! A partir de ahora, el estado de voz como el que tienes ahora se nombrará según tu plantilla. Deberías verlo actualizarse en unos segundos.",
"createSongRequestChannel": "¡Se ha creado un canal de solicitudes de canciones ({0})! Puedes empezar a solicitar cualquier canción por nombre o URL en ese canal, sin necesidad de usar el prefijo del bot."
}

175
langs/FR.json Normal file
View File

@@ -0,0 +1,175 @@
{
"unknownException": "⚠️ Une erreur est survenue lors de l'exécution de la commande ! Veuillez réessayer plus tard ou rejoignez notre serveur Discord pour plus d'assistance.",
"enabled": "activé",
"disabled": "désactivé",
"nodeReconnect": "Veuillez réessayer après la reconnexion du node.",
"noChannel": "Aucun salon vocal pour se connecter. Veuillez en fournir un ou rejoindre un salon vocal.",
"alreadyConnected": "Déjà connecté à un salon vocal.",
"noPermission": "Désolé ! Je n'ai pas les permissions pour rejoindre ou parler dans votre salon vocal.",
"noCreatePermission": "Désolé ! Je n'ai pas les permissions pour créer un salon de demande de chansons.",
"noPlaySource": "Impossible de trouver une source à lire !",
"noPlayer": "Aucun lecteur n'a été trouvé sur ce serveur.",
"notVote": "Cette commande nécessite votre vote ! Tapez `/vote` pour en savoir plus.",
"missingIntents": "Désolé, cette commande ne peut pas être exécutée car le bot n'a pas l'intent requis : `({0})`.",
"languageNotFound": "Aucun pack de langue trouvé ! Veuillez sélectionner un pack de langue existant.",
"changedLanguage": "Changement réussi vers le pack de langue `{0}`.",
"setPrefix": "C'est fait ! Mon préfixe dans votre serveur est désormais `{0}`. Essayez de lancer `{1}ping` pour tester.",
"setDJ": "Le DJ a été défini sur {0}.",
"setQueue": "Le mode file d'attente est défini sur `{0}`.",
"247": "Le mode 24/7 est maintenant `{0}`.",
"bypassVote": "Vous avez le système de vote `{0}`.",
"setVolume": "Le volume est réglé à `{0}`%",
"togglecontroller": "Vous avez maintenant `{0}` le contrôleur de musique.",
"toggleDuplicateTrack": "Vous avez maintenant `{0}` la prévention des morceaux en double.",
"toggleControllerMsg": "Vous avez maintenant `{0}` les messages du contrôleur musical.",
"toggleSilentMsg": "Vous avez {0} messages silencieux.",
"settingsMenu": "Paramètres du serveur | {0}",
"settingsTitle": "❤️ Infos de base :",
"settingsValue": "```Préfixe : {0}\nLangue : {1}\nContrôleur musique : {2}\nRôle DJ : @{3}\nVote Bypass : {4}\n24/7 : {5}\nVolume par défaut : {6}%\nTemps de lecture : {7}```",
"settingsTitle2": "🔗 Infos sur la file :",
"settingsValue2": "```Mode de file : {0}\nMaximum de chansons : {1}\nPiste dupliquée : {2}```",
"settingsTitle3": "🎤 Infos sur l'état vocal :",
"settingsPermTitle": "✨ Permissions :",
"settingsPermValue": "```{0} Administrateur\n{1} Gérer_serveur\n{2} Gérer_salons\n{3} Gérer_messages```",
"pingTitle1": "Infos Bot :",
"pingTitle2": "Infos Lecteur :",
"pingfield1": "```Shard ID : {0}/{1}\nLatence Shard : {2:.3f}s {3}\nRégion : {4}```",
"pingfield2": "```Node : {0} - {1:.3f}s\nLecteurs : {2}\nRégion vocale : {3}```",
"addEffect": "Effet `{0}` appliqué.",
"clearEffect": "Les effets sonores ont été supprimés !",
"FilterTagAlreadyInUse": "Cet effet sonore est déjà utilisé ! Veuillez utiliser /cleareffect <Tag> pour le retirer.",
"playlistViewTitle": "📜 Toutes les playlists de {0}",
"playlistViewHeaders": "ID:,Durée:,Nom:,Morceaux:",
"playlistFooter": "Tapez /playlist play [playlist] pour ajouter une playlist à la file.",
"playlistNotFound": "Playlist [`{0}`] introuvable. Tapez /playlist view pour voir toutes vos playlists.",
"playlistNotAccess": "Désolé ! Vous n'êtes pas autorisé à accéder à cette playlist !",
"playlistNoTrack": "Désolé ! Il n'y a aucun morceau dans la playlist [`{0}`].",
"playlistNotAllow": "Cette commande n'est pas autorisée sur les playlists liées ou partagées.",
"playlistPlay": "La playlist [`{0}`] avec `{1}` chansons a été ajoutée à la file.",
"playlistOverText": "Désolé ! Le nom de la playlist ne peut pas dépasser 10 caractères.",
"playlistSameName": "Désolé ! Veuillez choisir un nouveau nom pour la playlist.",
"playlistDeleteError": "Vous n'êtes pas autorisé à supprimer la playlist par défaut.",
"playlistRemove": "Vous avez supprimé la playlist [`{0}`].",
"playlistSendErrorPlayer": "Désolé ! Vous ne pouvez pas vous envoyer une invitation à vous-même.",
"playlistSendErrorBot": "Désolé ! Vous ne pouvez pas envoyer une invitation à un bot.",
"playlistBelongs": "Désolé ! Cette playlist appartient à <@{0}>.",
"playlistShare": "Désolé ! Cette playlist a été partagée avec {0}.",
"playlistSent": "Désolé ! Vous avez déjà envoyé une invitation à cet utilisateur auparavant.",
"noPlaylistAcc": "{0} n'a pas créé de compte playlist.",
"overPlaylistCreation": "Vous ne pouvez pas créer plus de `{0}` playlists !",
"playlistExists": "La playlist [`{0}`] existe déjà.",
"playlistNotInvalidUrl": "Veuillez entrer un lien valide de playlist publique Spotify ou YouTube.",
"playlistCreated": "Vous avez créé `{0}` playlists. Tapez /playlist view pour plus d'infos.",
"playlistRenamed": "Vous avez renommé `{0}` en `{1}`.",
"playlistLimitTrack": "Vous avez atteint la limite ! Vous ne pouvez ajouter que `{0}` chansons à votre playlist.",
"playlistPlaylistLink": "Vous n'êtes pas autorisé à utiliser des liens de playlist.",
"playlistStream": "Vous n'êtes pas autorisé à ajouter des vidéos en streaming à votre playlist.",
"playlistPositionNotFound": "Impossible de trouver la position `{0}` dans votre playlist [`{1}`] !",
"playlistRemoved": "👋 **{0}** a été retiré de la playlist [`{2}`] de {1}.",
"playlistClear": "Vous avez vidé avec succès votre playlist [`{0}`].",
"playlistView": "Visualiseur de playlist",
"playlistViewDesc": "```Nom | ID : {0} | {1}\nTotal Morceaux : {2}\nPropriétaire : {3}\nType : {4}\n```",
"playlistViewPermsValue": "📖 Lecture : ✓ ✍🏽 Écriture : {0} 🗑️ Suppression : {1}",
"playlistViewPermsValue2": "📖 Lecture : {0}",
"playlistViewTrack": "Morceaux",
"playlistViewPage": "Page : {0}/{1} | Durée totale : {2}",
"inboxFull": "Désolé ! La boîte de réception de {0} est pleine.",
"inboxNoMsg": "Aucun message dans votre boîte de réception.",
"invitationSent": "Invitation envoyée à {0}.",
"notInChannel": "{0}, vous devez être dans {1} pour utiliser les commandes vocales. Veuillez rejoindre à nouveau si vous êtes déjà en vocal !",
"noTrackPlaying": "Aucune chanson n'est en cours de lecture pour le moment",
"noTrackFound": "Aucune chanson trouvée pour cette requête ! Veuillez fournir une URL valide.",
"noLinkSupport": "La commande de recherche ne prend pas en charge les liens !",
"voted": "Vous avez voté !",
"missingPerms_pos": "Seul le DJ ou les admins peuvent changer la position.",
"missingPerms_mode": "Seul le DJ ou les admins peuvent changer le mode de boucle.",
"missingPerms_queue": "Seul le DJ ou les admins peuvent retirer un morceau de la file.",
"missingPerms_autoplay": "Seul le DJ ou les admins peuvent activer/désactiver l'autoplay !",
"missingPerms_function": "Seul le DJ ou un admin peut utiliser cette fonction.",
"timeFormatError": "Format de temps incorrect. Exemple : 2:42 ou 12:39:31",
"lyricsNotFound": "Paroles non trouvées. Tapez /lyrics <Nom de la Chanson> <Auteur> pour chercher les paroles.",
"missingTrackInfo": "Certaines informations sur le morceau sont manquantes.",
"noVoiceChannel": "Aucun salon vocal trouvé !",
"playlistAddError": "Vous n'êtes pas autorisé à ajouter des vidéos en streaming à votre playlist !",
"playlistAddError2": "Problème lors de l'ajout de morceaux à la playlist !",
"playlistlimited": "Vous avez atteint la limite ! Vous ne pouvez ajouter que {0} chansons à votre playlist.",
"playlistrepeated": "Ce morceau est déjà dans votre playlist !",
"playlistAdded": "❤️ **{0}** ajouté à la playlist [`{2}`] de {1} !",
"playerDropdown": "Sélectionnez une chanson vers laquelle passer...",
"playerFilter": "Sélectionnez un filtre à appliquer...",
"buttonBack": "Précédent",
"buttonPause": "Pause",
"buttonResume": "Reprendre",
"buttonSkip": "Passer",
"buttonLeave": "Quitter",
"buttonLoop": "Boucle",
"buttonVolumeUp": "Volume +",
"buttonVolumeDown": "Volume -",
"buttonVolumeMute": "Muet",
"buttonVolumeUnmute": "Son activé",
"buttonAutoPlay": "Autoplay",
"buttonShuffle": "Mélanger",
"buttonForward": "Avancer",
"buttonRewind": "Rembobiner",
"buttonLyrics": "Paroles",
"nowplayingDesc": "**Lecture en cours :**\n```{0}```",
"nowplayingField": "À suivre :",
"nowplayingLink": "Écouter sur {0}",
"connect": "Connecté à {0}",
"live": "EN DIRECT",
"playlistLoad": " 🎶 La playlist **{0}** avec `{1}` chansons a été ajoutée à la file.",
"trackLoad": "**[{0}](<{1}>)** de **{2}** (`{3}`) a été ajoutée et commencera à jouer.\n",
"trackLoad_pos": "**[{0}](<{1}>)** de **{2}** (`{3}`) a été ajoutée à la file à la position **{4}**\n",
"searchTitle": "Recherche : {0}",
"searchDesc": "➥ Plateforme : {0} **{1}**\n➥ Résultats : **{2}**\n\n{3}",
"searchWait": "Choisissez la chanson à ajouter à la file.",
"searchTimeout": "Recherche expirée. Veuillez réessayer plus tard.",
"searchSuccess": "Chanson ajoutée à la file.",
"queueTitle": "File à venir :",
"historyTitle": "Historique :",
"viewTitle": "File de musiques",
"viewDesc": "**Lecture en cours : [Cliquez ici]({0}) ⮯**\n{1}",
"viewFooter": "Page : {0}/{1} | Durée totale : {2}",
"pauseError": "Le lecteur est déjà en pause.",
"pauseVote": "{0} a voté pour mettre la chanson en pause. [{1}/{2}]",
"paused": "`{0}` a mis le lecteur en pause.",
"resumeError": "Le lecteur n'est pas en pause.",
"resumeVote": "{0} a voté pour reprendre la chanson. [{1}/{2}]",
"resumed": "`{0}` a repris le lecteur.",
"shuffleError": "Ajoutez plus de chansons à la file avant de mélanger.",
"shuffleVote": "{0} a voté pour mélanger la file. [{1}/{2}]",
"shuffled": "La file a été mélangée.",
"skipError": "Aucune chanson vers laquelle passer.",
"skipVote": "{0} a voté pour passer la chanson. [{1}/{2}]",
"skipped": "`{0}` a passé la chanson.",
"backVote": "{0} a voté pour passer à la chanson précédente. [{1}/{2}]",
"backed": "`{0}` est en train de passer à la chanson précédente.",
"leaveVote": "{0} a voté pour arrêter le lecteur. [{1}/{2}]",
"left": "`{0}` a arrêté le lecteur.",
"seek": "Le lecteur a été placé à **{0}**",
"repeat": "Le mode de répétition a été réglé sur `{0}`",
"cleared": "Tous les morceaux dans `{0}` ont été supprimés",
"removed": "`{0}` morceaux ont été retirés de la file.",
"forward": "Le lecteur a avancé à **{0}**",
"rewind": "Le lecteur a été rembobiné à **{0}**",
"replay": "Relecture de la chanson actuelle.",
"swapped": "`{0}` et `{1}` ont été échangés",
"moved": "`{0}` a été déplacé vers `{1}`",
"autoplay": "Le mode autoplay est maintenant **{0}**",
"notdj": "Vous n'êtes pas DJ. Le DJ actuel est {0}.",
"djToMe": "Impossible de transférer le rôle de DJ à vous-même ou à un bot.",
"djnotinchannel": "`{0}` n'est pas dans le salon vocal.",
"djswap": "Vous avez transféré le rôle de DJ à `{0}`.",
"chaptersDropdown": "Sélectionnez un chapitre vers lequel passer ...",
"noChaptersFound": "Aucun chapitre trouvé !",
"chatpersNotSupport": "Cette commande ne supporte que les vidéos YouTube !",
"voicelinkQueueFull": "Désolé, vous avez atteint le maximum de `{0}` morceaux dans la file !",
"voicelinkOutofList": "Veuillez fournir un index de morceau valide !",
"voicelinkDuplicateTrack": "Désolé, ce morceau est déjà dans la file.",
"deocdeError": "Une erreur s'est produite lors du décodage du fichier !",
"invalidStartTime": "Heure de début invalide, doit être entre `00:00` et `{0}`",
"invalidEndTime": "Heure de fin invalide, doit être entre `00:00` et `{0}`",
"invalidTimeOrder": "L'heure de fin ne peut pas être inférieure ou égale à celle de début",
"setStageAnnounceTemplate": "C'est fait ! Désormais, le statut vocal tel que celui dans lequel vous êtes sera nommé selon votre modèle. Vous devriez le voir se mettre à jour dans quelques secondes.",
"createSongRequestChannel": "Un salon de demande de chansons ({0}) a été créé ! Vous pouvez commencer à demander n'importe quelle chanson par nom ou URL dans ce salon, sans besoin d'utiliser le préfixe du bot."
}

View File

@@ -2,7 +2,6 @@
"unknownException": "⚠️ コマンドの実行中に何かが間違っています後でもう一度試してください。または、より詳しいサポートを求めるために当社のDiscordサーバーに参加してください。",
"enabled": "有効化",
"disabled": "無効化",
"nodeReconnect": "再試行してください!ノードが再接続した後。",
"noChannel": "接続する音声チャンネルがありません。提供するか、参加してください。",
"alreadyConnected": "すでに音声チャンネルに接続しています。",
@@ -16,13 +15,14 @@
"changedLanguage": "「{0}」言語パックに正常に変更しました。",
"setPrefix": "完了!あなたのサーバーのプレフィックスは今や「{0}」です。 `{1}ping`を実行してテストしてみてください。",
"setDJ": "{0}にDJを設定しました。",
"setqueue": "キューモードを「{0}」に設定しました。",
"setQueue": "キューモードを「{0}」に設定しました。",
"247": "今、24/7モードは「{0}」です。",
"bypassVote": "今、投票システムは「{0}」になりました。",
"setVolume": "音量を「{0}%」に設定しました。",
"togglecontroller": "今、音楽コントローラーは「{0}」です。",
"toggleDuplicateTrack": "今、キュー内の重複トラックを防止するための設定は「{0}」です。",
"toggleControllerMsg": "現在、音楽コントローラーから `{0}` 件のメッセージがあります。",
"toggleSilentMsg": "あなたは{0}サイレントメッセージを持っています。",
"settingsMenu": "サーバー設定| {0}",
"settingsTitle": "❤️ 基本情報:",
"settingsValue": "```プレフィックス:{0}\n言語{1}\n音楽コントローラーを有効にする{2}\nDJロール@{3}\n投票バイパス{4}\n24/7{5}\nデフォルトボリューム{6}\n再生時間{7}```",
@@ -38,7 +38,6 @@
"addEffect": "`{0}` フィルターを適用します。",
"clearEffect": "効果音がクリアされました!",
"FilterTagAlreadyInUse": "このサウンドエフェクトはすでに使用されています!削除するには/cleareffect <Tag>を使用してください。",
"playlistViewTitle": "📜 {0}のすべてのプレイリスト",
"playlistViewHeaders": "ID:,時間:,名前:,トラック:",
"playlistFooter": "プレイリストをキューに追加するには、/playlist play [playlist]を入力してください。",
@@ -77,7 +76,6 @@
"inboxFull": "申し訳ありません!{0}さんの受信トレイはいっぱいです。",
"inboxNoMsg": "受信トレイにメッセージはありません。",
"invitationSent": "{0}さんに招待状を送信しました。",
"notInChannel": "{0}さん、音声コマンドを使用するには{1}にいる必要があります。音声に接続している場合は再接続してください!",
"noTrackPlaying": "現在再生中の曲はありません",
"noTrackFound": "そのクエリで曲が見つかりませんでした有効なURLを入力してください。",
@@ -92,16 +90,13 @@
"lyricsNotFound": "歌詞が見つかりませんでした。/lyrics <曲名> <アーティスト>と入力して歌詞を検索してください。",
"missingTrackInfo": "一部のトラック情報が欠落しています。",
"noVoiceChannel": "音声チャンネルが見つかりません!",
"playlistAddError": "ストリーミングビデオをプレイリストに追加することはできません!",
"playlistAddError2": "トラックをプレイリストに追加する際に問題が発生しました!",
"playlistlimited": "制限に達しました!プレイリストには{0}曲しか追加できません。",
"playlistrepeated": "すでにプレイリストに同じトラックがあります!",
"playlistAdded": "❤️ **{0}**を{1}のプレイリスト[`{2}`]に追加しました!",
"playerDropdown": "スキップする曲を選択してください...",
"playerFilter": "適用するフィルターを選択してください...",
"buttonBack": "戻る",
"buttonPause": "一時停止",
"buttonResume": "再開",
@@ -117,30 +112,24 @@
"buttonForward": "進む",
"buttonRewind": "戻る",
"buttonLyrics": "歌詞",
"nowplayingDesc": "**現在再生中:**\n```{0}```",
"nowplayingField": "次に再生する曲:",
"nowplayingLink": "{0}で聴く",
"connect": "{0}に接続しました",
"live": "ライブ",
"playlistLoad": " 🎶 プレイリスト**{0}**をキューに`{1}`曲追加しました。",
"trackLoad": "**{2}**の**[{0}](<{1}>)** (`{3}`)を再生を開始するために追加しました。\n",
"trackLoad_pos": "**{2}**の**[{0}](<{1}>)** (`{3}`)をキューの位置**{4}**に追加しました。\n",
"searchTitle": "検索クエリ:{0}",
"searchDesc": "➥ プラットフォーム:{0} **{1}**\n➥ 結果:**{2}**\n\n{3}",
"searchWait": "キューに追加したい曲を選択してください。",
"searchTimeout": "検索がタイムアウトしました。後でもう一度お試しください。",
"searchSuccess": "曲がキューに追加されました。",
"queueTitle": "キューに追加された曲:",
"historyTitle": "再生履歴:",
"viewTitle": "音楽キュー",
"viewDesc": "**現在再生中:[ここをクリックして聴く]({0}) ⮯**\n{1}",
"viewFooter": "ページ:{0}/{1} | 合計再生時間:{2}",
"pauseError": "プレイヤーはすでに一時停止しています。",
"pauseVote": "{0}が曲を一時停止することに賛成しました。[{1}/{2}]",
"paused": "{0}がプレイヤーを一時停止しました。",
@@ -153,13 +142,10 @@
"skipError": "スキップする曲はありません。",
"skipVote": "{0}が曲をスキップすることに賛成しました。[{1}/{2}]",
"skipped": "{0}が曲をスキップしました。",
"backVote": "{0}が前の曲にスキップすることに賛成しました。[{1}/{2}]",
"backed": "`{0}`が前の曲にスキップしました。",
"leaveVote": "{0}がプレイヤーを停止することに賛成しました。[{1}/{2}]",
"left": "`{0}`がプレイヤーを停止しました。",
"seek": "プレイヤーを **{0}** に設定しました。",
"repeat": "リピートモードが `{0}` に設定されました。",
"cleared": "`{0}`のすべてのトラックをクリアしました。",
@@ -170,25 +156,20 @@
"swapped": "`{0}`と`{1}`が交換されました。",
"moved": "`{0}`を`{1}`に移動しました。",
"autoplay": "オートプレイモードが **{0}** に設定されました。",
"notdj": "DJではありません。現在のDJは{0}です。",
"djToMe": "自分自身またはボットにDJ権限を移行することはできません。",
"djnotinchannel": "`{0}`はボイスチャンネルにいません。",
"djswap": "DJの役割を`{0}`に移行しました。",
"chaptersDropdown": "スキップする章を選択してください...",
"noChaptersFound": "章が見つかりませんでした!",
"chatpersNotSupport": "このコマンドはYouTubeの動画のみサポートしています",
"voicelinkQueueFull": "申し訳ありませんが、キュー内の曲数が最大値の`{0}`に達しました!",
"voicelinkOutofList": "有効なトラックインデックスを指定してください!",
"voicelinkDuplicateTrack": "申し訳ありませんが、このトラックは既にキューに存在します。",
"deocdeError": "ファイルのデコード中に問題が発生しました!",
"invalidStartTime": "無効な開始時間!時間は `00:00` と `{0}` の間に設定する必要があります。",
"invalidEndTime": "無効な終了時間!時間は `00:00` と `{0}` の間に設定する必要があります。",
"invalidTimeOrder": "終了時間は開始時間より大きくない必要があります。",
"setStageAnnounceTemplate": "完了!これからは、今いるボイスステータスがあなたのテンプレートに従って名前が付けられます。数秒以内に更新されるのを見ることができるはずです。",
"createSongRequestChannel": "曲のリクエストチャンネル ({0}) が作成されました!そのチャンネルで、曲名または URL を使用して任意の曲をリクエストできます。ボットのプレフィックスは必要ありません。"
}

View File

@@ -2,7 +2,6 @@
"unknownException": "⚠️ 명령어 실행 중 오류가 발생했습니다! 나중에 다시 시도하거나 추가 지원을 위해 디스코드 서버에 참여하십시오.",
"enabled": "활성화됨",
"disabled": "비활성화됨",
"nodeReconnect": "다시 시도하세요! 노드가 다시 연결될 때까지 기다려주세요.",
"noChannel": "연결할 음성 채널이 없습니다. 하나를 제공하거나 참여하십시오.",
"alreadyConnected": "이미 음성 채널에 연결되어 있습니다.",
@@ -16,13 +15,14 @@
"changedLanguage": "성공적으로 `{0}` 언어 팩으로 변경되었습니다.",
"setPrefix": "완료되었습니다! 이 서버에서 내 접두사는 이제 `{0}`입니다. `{1}ping`을 실행하여 테스트해보세요.",
"setDJ": "DJ를 {0}(으)로 설정했습니다.",
"setqueue": "큐 모드를 `{0}`(으)로 설정했습니다.",
"setQueue": "큐 모드를 `{0}`(으)로 설정했습니다.",
"247": "이제 24/7 모드에서 `{0}`으로 변경되었습니다.",
"bypassVote": "이제 `{0}` 투표 시스템을 사용할 수 있습니다.",
"setVolume": "볼륨을 `{0}`%로 설정했습니다.",
"togglecontroller": "이제 음악 컨트롤러가 `{0}`(으)로 설정되었습니다.",
"toggleDuplicateTrack": "이제 대기열에서 중복된 트랙을 방지하기 위해 `{0}`(으)로 설정되었습니다.",
"toggleControllerMsg": "이제 음악 컨트롤러로부터 `{0}` 메시지가 있습니다.",
"toggleSilentMsg": "당신은 {0} 침묵 메시지를 사용하고 있습니다.",
"settingsMenu": "서버 설정 | {0}",
"settingsTitle": "❤️ 기본 정보:",
"settingsValue": "```접두사: {0}\n언어: {1}\n음악 컨트롤러 사용: {2}\nDJ 역할: @{3}\n투표 우회: {4}\n24/7 모드: {5}\n기본 볼륨: {6}%\n재생 시간: {7}```",
@@ -38,7 +38,6 @@
"addEffect": "`{0}` 필터를 적용하세요.",
"clearEffect": "효과가 삭제되었습니다!",
"FilterTagAlreadyInUse": "이 필터는 이미 사용 중입니다! 삭제하려면 /cleareffect <Tag>를 사용하십시오.",
"playlistViewTitle": "📜 {0}의 모든 재생 목록",
"playlistViewHeaders": "ID:,시간:,이름:,트랙:",
"playlistFooter": "/playlist play [재생 목록]을 입력하여 재생 목록을 대기열에 추가하세요.",
@@ -77,7 +76,6 @@
"inboxFull": "죄송합니다! {0}님의 받은 편지함이 가득 찼습니다.",
"inboxNoMsg": "받은 편지함에 메시지가 없습니다.",
"invitationSent": "{0}님에게 초대장을 보냈습니다.",
"notInChannel": "{0}, 음성 명령을 사용하려면 {1}에 있어야합니다. 음성 채널에 있지 않으면 다시 참여하세요!",
"noTrackPlaying": "현재 재생중인 곡이 없습니다.",
"noTrackFound": "해당 쿼리로 곡을 찾을 수 없습니다. 유효한 URL을 제공해주세요.",
@@ -92,16 +90,13 @@
"lyricsNotFound": "가사를 찾을 수 없습니다. 가사를 찾으려면 /lyrics <노래 제목> <작곡가>를 입력하세요.",
"missingTrackInfo": "일부 트랙 정보가 누락되었습니다.",
"noVoiceChannel": "음성 채널을 찾을 수 없습니다!",
"playlistAddError": "스트리밍 비디오를 재생목록에 추가할 수 없습니다!",
"playlistAddError2": "재생목록에 곡을 추가하는 중 문제가 발생했습니다!",
"playlistlimited": "한 재생목록에 최대 {0}곡까지 추가 가능합니다. 이제 한도에 도달했습니다!",
"playlistrepeated": "재생목록에 이미 같은 곡이 있습니다!",
"playlistAdded": "❤️ **{0}**을(를) {1}의 재생목록 [`{2}`] 에 추가했습니다!",
"playerDropdown": "건너뛰기 할 노래를 선택하세요...",
"playerFilter": "적용할 필터를 선택하세요...",
"buttonBack": "이전",
"buttonPause": "일시정지",
"buttonResume": "재개",
@@ -117,30 +112,24 @@
"buttonForward": "앞으로",
"buttonRewind": "뒤로",
"buttonLyrics": "가사",
"nowplayingDesc": "**현재 재생중인 곡:**\n```{0}```",
"nowplayingField": "다음 곡:",
"nowplayingLink": "{0}에서 듣기",
"connect": "{0}에 연결되었습니다.",
"live": "라이브",
"playlistLoad": "재생목록 **{0}**을(를) 대기열에 `{1}`개의 곡과 함께 추가했습니다.",
"trackLoad": "**{2}**의 **[{0}](<{1}>)** (`{3}`)를 재생목록에 추가하고 재생을 시작합니다.\n",
"trackLoad_pos": "**{2}**의 **[{0}](<{1}>)** (`{3}`)를 대기열의 **{4}**번째로 추가합니다.\n",
"searchTitle": "검색 쿼리: {0}",
"searchDesc": "➥ 플랫폼: {0} **{1}**\n➥ 결과: **{2}**\n\n{3}",
"searchWait": "대기열에 추가할 곡을 선택하세요.",
"searchTimeout": "검색이 시간 초과되었습니다. 나중에 다시 시도해주세요.",
"searchSuccess": "곡이 대기열에 추가되었습니다.",
"queueTitle": "다음 대기열:",
"historyTitle": "이전 곡 대기열:",
"viewTitle": "음악 대기열",
"viewDesc": "**현재 재생중: [여기를 클릭하여 듣기]({0}) ⮯**\n{1}",
"viewFooter": "페이지: {0}/{1} | 총 재생 시간: {2}",
"pauseError": "플레이어가 이미 일시정지되었습니다.",
"pauseVote": "{0}님이 노래 일시정지 투표를 했습니다. [{1}/{2}]",
"paused": "{0}님이 플레이어를 일시정지했습니다.",
@@ -153,13 +142,10 @@
"skipError": "건너뛸 노래가 없습니다.",
"skipVote": "{0}님이 노래 건너뛰기 투표를 했습니다. [{1}/{2}]",
"skipped": "{0}님이 노래를 건너뛰었습니다.",
"backVote": "{0}님이 이전 노래로 건너뛰기 투표를 했습니다. [{1}/{2}]",
"backed": "`{0}`님이 이전 노래로 건너뛰었습니다.",
"leaveVote": "{0}님이 플레이어를 중지하기 투표를 했습니다. [{1}/{2}]",
"left": "`{0}`님이 플레이어를 중지했습니다.",
"seek": "플레이어가 **{0}**로 설정되었습니다.",
"repeat": "반복 모드가 `{0}`(으)로 설정되었습니다.",
"cleared": " `{0}`의 모든 트랙이 삭제되었습니다.",
@@ -170,25 +156,20 @@
"swapped": "`{0}`와 `{1}`이(가) 스왑되었습니다.",
"moved": "`{0}`을(를) `{1}`로 이동했습니다.",
"autoplay": "자동 재생 모드가 **{0}**(으)로 변경되었습니다.",
"notdj": "당신은 DJ가 아닙니다. 현재 DJ는 {0}입니다.",
"djToMe": "자신이나 봇에게 DJ를 이전할 수 없습니다.",
"djnotinchannel": "`{0}`님이 음성 채널에 없습니다.",
"djswap": "당신은 DJ 권한을 `{0}`님에게 이전했습니다.",
"chaptersDropdown": "건너뛸 챕터를 선택해주세요.",
"noChaptersFound": "챕터를 찾을 수 없습니다!",
"chatpersNotSupport": "이 명령어는 유튜브 비디오만 지원합니다!",
"voicelinkQueueFull": "죄송합니다. 큐에 `{0}`개의 트랙을 모두 추가하셨습니다!",
"voicelinkOutofList": "유효한 트랙 인덱스를 제공해주세요!",
"voicelinkDuplicateTrack": "죄송합니다. 이 트랙은 이미 큐에 있습니다.",
"deocdeError": "파일 디코딩 중 문제가 발생했습니다!",
"invalidStartTime": "효력 없는 시작 시간! 시간은 `00:00` 과 `{0}` 사이에 설정해야 합니다.",
"invalidEndTime": "효력 없는 종료 시간! 시간은 `00:00` 과 `{0}` 사이에 설정해야 합니다.",
"invalidTimeOrder": "종료 시간은 시작 시간보다 클수 있어야 합니다.",
"setStageAnnounceTemplate": "완료! 이제부터 지금 있는 음성 상태는 귀하의 템플릿에 따라 이름이 지정됩니다. 몇 초 후에 업데이트되는 것을 볼 수 있을 것입니다.",
"createSongRequestChannel": "노래 요청 채널 ({0})이 생성되었습니다! 해당 채널에서 노래 제목이나 URL로 원하는 노래를 요청할 수 있으며, 봇 접두사를 사용할 필요가 없습니다."
}

View File

@@ -1,8 +1,7 @@
{
{
"unknownException": "⚠️ Coś poszło nie tak podczas wykonywania polecenia. Spróbuj ponownie później lub dołąćz do serwera discord w celu uzyskania pomocy.",
"enabled": "włączony",
"disabled": "wyłączony",
"nodeReconnect": "Spróbuj ponownie! Trwa ponowne łącznie z node'm.",
"noChannel": "Brak kanału głosowego do dołącznia. Dołącz do jakiegoś, lub wskaż kanał komendą /connect",
"alreadyConnected": "Już połączono z kanałem głosowym.",
@@ -16,13 +15,14 @@
"changedLanguage": "Pomyślnie zmieniono pakiet językowy na: `{0}` .",
"setPrefix": "Gotowe! Mój prefix na tym serwerze to teraz `{0}`. Spróbuj uruchomić polecenie `{1}ping`, aby go przetestować.",
"setDJ": "Ustawiono DJ jako {0}.",
"setqueue": "Zmienionio tryb kolejki na `{0}`.",
"setQueue": "Zmienionio tryb kolejki na `{0}`.",
"247": "Tryb 24/7: `{0}`",
"bypassVote": "System głosowania: `{0}`",
"setVolume": "Ustawiono głośność na `{0}`%",
"togglecontroller": "Kontroler muzyki: `{0}`",
"toggleDuplicateTrack": "Unikanie duplikowania pozycji w kolejce: `{0}`",
"toggleControllerMsg": "Wiadomości od kontrolera muzyki: `{0}`",
"toggleSilentMsg": "Masz {0} ciche wiadomości.",
"settingsMenu": "Ustawnienia serwera | {0}",
"settingsTitle": "❤️ Podstawowe informacje:",
"settingsValue": "```Prefix: {0}\nJęzyk: {1}\nKontroler muzyki: {2}\nRola DJ: @{3}\nOminięcie głosowania: {4}\n24/7: {5}\nDomyślna głośność: {6}%\nCzas odtwarzania: {7}```",
@@ -38,7 +38,6 @@
"addEffect": "Nałożono efekt: `{0}`",
"clearEffect": "Efekty dźwiękowe zostały wyczyszczone.",
"FilterTagAlreadyInUse": "Ten efekt dźwiękowy jest już w używany! Użyj /cleareffect <Efekt> aby go wyłączyć.",
"playlistViewTitle": "📜 Playlisty użytkownika {0}:",
"playlistViewHeaders": "ID:,Czas:,Nazwa:,Ilość pozycji:",
"playlistFooter": "Użyj /playlist play [nazwa_playlisty] by dodać playlistę do kolejki.",
@@ -77,7 +76,6 @@
"inboxFull": "Błąd! Skrzynka odbiorcza {0} jest pełna.",
"inboxNoMsg": "W twojej skrzynce odbiorczej nie ma wiadomości.",
"invitationSent": "Zaproszenie wysłane do {0}.",
"notInChannel": "{0}, musisz być na kanale {1} by używać komend głosowych. Jeżeli jesteś na kanale połącz się ponownie!",
"noTrackPlaying": "Aktualnie nic nie jest odtwarzane",
"noTrackFound": "Nie znaleziono utworu! Podaj poprawny adres URL.",
@@ -92,16 +90,13 @@
"lyricsNotFound": "Tekst nie został znaleziony. Użyj /lyrics <nazwa utworu> <autor> by znaleźć tekst piosenki.",
"missingTrackInfo": "Brakuje niektórych informacji o utworze.",
"noVoiceChannel": "Kanał nie został znaleziony",
"playlistAddError": "Niemożesz dodawać transmisji na żywo do playlisty!",
"playlistAddError2": "Podczas dodawania utworu do playlisty wystąpił błąd!",
"playlistlimited": "Osiągnąłeś limit! Możesz dodać maksymalnie `{0}` pozycji do playlisty.",
"playlistrepeated": "Ten utwór znajduje sie już na playliście!",
"playlistAdded": "❤️ Dodano **{0}** do playlisty [`{2}`] użytkownika {1}!",
"playerDropdown": "Wybierz pozycję do której pominąć ...",
"playerFilter": "Wybierz efekt który chesz nałożyć ...",
"buttonBack": "Cofnij",
"buttonPause": "Pauza",
"buttonResume": "Wznów",
@@ -117,30 +112,24 @@
"buttonForward": "Przewiń do przodu",
"buttonRewind": "Przewiń do tyłu",
"buttonLyrics": "Tekst utworu",
"nowplayingDesc": "**Aktualnie odtwarzane:**\n```{0}```",
"nowplayingField": "Następne:",
"nowplayingLink": "Słuchaj na: {0}",
"connect": "Połączono do {0}",
"live": "LIVE",
"playlistLoad": " 🎶 Dodano playlistę **{0}** z `{1}` pozycjami do kolejki.",
"trackLoad": "Rozpoczęto odtwarzanie **[{0}](<{1}>)** autorstwa **{2}** (`{3}`).\n",
"trackLoad_pos": "Dodano **[{0}](<{1}>)** autorstwa **{2}** (`{3}`) do kolejki na pozycji **{4}**\n",
"searchTitle": "Wyszukiwana fraza: {0}",
"searchDesc": "➥ Platforma: {0} **{1}**\n➥ Wyniki: **{2}**\n\n{3}",
"searchWait": "Wybierz utwór, który chcesz dodać do playlisty.",
"searchTimeout": "Upłynął czas wyszukiwania. Spróbuj ponownie później.",
"searchSuccess": "Dodano utwór do kolejki.",
"queueTitle": "Nadchodząca kolejka:",
"historyTitle": "Historia kolejki:",
"viewTitle": "Kolejka utworów",
"viewDesc": "**Aktualnie odtwarzane: [Kliknij mnie]({0}) ⮯**\n{1}",
"viewFooter": "Strona: {0}/{1} | Całkowity czas trwania: {2}",
"pauseError": "Odtwarzacz jest już zapauzowany.",
"pauseVote": "{0} zagłosował nad zapauzowaniem odtwarzania. [{1}/{2}]",
"paused": "`{0}` zapauzował odtwarznaie.",
@@ -153,13 +142,10 @@
"skipError": "Nie ma utworu, do którego można by pominąć.",
"skipVote": "{0} zagłosował za pominięciem tego utworu. [{1}/{2}]",
"skipped": "`{0}` pominął utwór.",
"backVote": "{0} zagłosował za przejściem do poprzedniego utworu. [{1}/{2}]",
"backed": "`{0}` cofnął do poprzedniego utworu.",
"leaveVote": "{0} zagłosował nad zatrzyamniem odtwarzaza. [{1}/{2}]",
"left": "`{0}` zatrzymał odtwarzacz.",
"seek": "Ustawiono odtwarzacz na **{0}**",
"repeat": "Tryb pętli został ustawiony na: `{0}`",
"cleared": "Wyczyszczono wszystkie utwory w: `{0}`",
@@ -170,25 +156,20 @@
"swapped": "`{0}` oraz `{1}` są zamienione",
"moved": "Przeniesiono `{0}` do `{1}`",
"autoplay": "Tryb autoodwarzania jest teraz **{0}**",
"notdj": "Nie jesteś DJ'em. Obecnym DJ'em jest {0}.",
"djToMe": "Nie możesz przekazać roli DJ'a sobie lub botu.",
"djnotinchannel": "`{0}` nie jest na kanale głosowym.",
"djswap": "Przekazałeś rolę DJ'a użytkownikowi `{0}`.",
"chaptersDropdown": "Wybierz rozdział do którego przeskoczyć ...",
"noChaptersFound": "Nie znaleziono żadnych rozdziałów!",
"chatpersNotSupport": "Ta komenda obsługuje jedynie utwory z YouTube'a!",
"voicelinkQueueFull": "Przepraszamy osiągnięto limit `{0}` pozycji w kolejce!",
"voicelinkOutofList": "Podaj właściwy indeks utworu!",
"voicelinkDuplicateTrack": "Błąd! Ten utwór znajduje się już w kolejce.",
"deocdeError": "Coś poszło nie tak podczas dekodowania pliku!",
"invalidStartTime": "Niepoprawny czas startu, musi on mieścić się pomiędzy `00:00` a `{0}`",
"invalidEndTime": "Niepoprawny czas końca, musi on mieścić się pomiędzy `00:00` a `{0}`",
"invalidTimeOrder": "Czas zakończenia odtwarzania nie może być mniejszy lub równy czasowi startu",
"setStageAnnounceTemplate": "Gotowe! Od tego momentu status kanału w którym znajduje się bot będzie wyglądał tak jak podany szablon. Aktualizacja powinna nastąpić za kilka sekund.",
"createSongRequestChannel": "Utworzono kanał do obsługi bota - ({0})! Możesz w nim dodawać utwory po nazwie lub adresie URL bez dodawnia prefixu"
}

View File

@@ -2,7 +2,6 @@
"unknownException": "⚠️ Что-то пошло не так при выполнении команды! Пожалуйста, попробуйте позже или присоединитесь к нашему серверу Discord для получения дополнительной поддержки.",
"enabled": "вкл",
"disabled": "выкл",
"nodeReconnect": "Пожалуйста, попробуйте чуть позже!",
"noChannel": "Нет голосового канала для подключения. Пожалуйста, укажите или присоединитесь к одному.",
"alreadyConnected": "Уже подключен к голосовому каналу.",
@@ -16,13 +15,14 @@
"changedLanguage": "Язык успешно изменен на `{0}`.",
"setPrefix": "Готово! Мой префикс на вашем сервере теперь `{0}`. Попробуйте запустить `{1}ping`, чтобы проверить его.",
"setDJ": "Установить роль DJ {0}.",
"setqueue": "Установить режим очереди на `{0}`.",
"setQueue": "Установить режим очереди на `{0}`.",
"247": "Теперь у вас `{0}` режим 24/7.",
"bypassVote": "Теперь у вас `{0}` система голосования.",
"setVolume": "Установить громкость на `{0}`%",
"togglecontroller": "Теперь у вас `{0}` контроллер музыки.",
"toggleDuplicateTrack": "Теперь у вас `{0}` фильтр дубликатов в очереди.",
"toggleControllerMsg": "Теперь у вас `{0}` сообщения от плеера.",
"toggleSilentMsg": "У вас {0} тихих сообщений.",
"settingsMenu": "Настройки сервера | {0}",
"settingsTitle": "❤️ Основная информация:",
"settingsValue": "```Префикс: {0}\nЯзык: {1}\nВключить контроллер музыки: {2}\nDJ роль: @{3}\nОбход голосования: {4}\n24/7: {5}\nГромкость по умолчанию: {6}%\nВремя проигрывания: {7}```",
@@ -38,7 +38,6 @@
"addEffect": "Применен `{0}` эффект.",
"clearEffect": "Звуковые эффекты были очищены!",
"FilterTagAlreadyInUse": "Этот звуковой эффект уже используется! Пожалуйста, используйте /cleareffect <Тег>, чтобы удалить его.",
"playlistViewTitle": "📜 Все плейлисты пользователя {0}",
"playlistViewHeaders": "ID:,Время:,Название:,Треки:",
"playlistFooter": "Введите /playlist play [плейлист], чтобы добавить плейлист в очередь.",
@@ -77,7 +76,6 @@
"inboxFull": "Извините! Почтовый ящик {0} переполнен.",
"inboxNoMsg": "В вашем почтовом ящике нет сообщений.",
"invitationSent": "Приглашение отправлено {0}.",
"notInChannel": "{0}, для использования голосовых команд вы должны находиться в {1}. Пожалуйста, перезайдите, если вы уже находитесь в голосовом канале!",
"noTrackPlaying": "Сейчас нет играющих треков.",
"noTrackFound": "Треки по такому запросу не найдены! Пожалуйста, укажите действительную ссылку.",
@@ -92,16 +90,13 @@
"lyricsNotFound": "Текст песни не найден. Введите /lyrics <Название песни> <Автор> для поиска текста.",
"missingTrackInfo": "Некоторая информация о треке отсутствует.",
"noVoiceChannel": "Голосовой канал не найден!",
"playlistAddError": "Вам не разрешено добавлять потоковые видео в плейлист!",
"playlistAddError2": "Произошла ошибка при добавлении треков в плейлист!",
"playlistlimited": "Вы достигли лимита! Вы можете добавить только {0} треков в свой плейлист.",
"playlistrepeated": "Такой трек уже есть в плейлисте!",
"playlistAdded": "❤️ Добавлен **{0}** в плейлист пользователя {1} [`{2}`]!",
"playerDropdown": "Выберите трек для перехода ...",
"playerFilter": "Выберите фильтр для применения ...",
"buttonBack": "Назад",
"buttonPause": "Пауза",
"buttonResume": "Пуск",
@@ -117,13 +112,10 @@
"buttonForward": "+10сек",
"buttonRewind": "Назад",
"buttonLyrics": "Тексты песен",
"nowplayingDesc": "**Сейчас играет:**\n```{0}```",
"nowplayingField": "Следующее:",
"nowplayingLink": "Слушать на {0}",
"connect": "Подключено к {0}",
"live": "В ЭФИРЕ",
"playlistLoad": "🎶 Добавлен плейлист **{0}** с `{1}` треками в очередь.",
"trackLoad": "Добавлен **[{0}](<{1}>)** от **{2}** (`{3}`) в очередь.\n",
@@ -133,13 +125,11 @@
"searchWait": "Выберите трек, который хотите добавить в очередь.",
"searchTimeout": "Поиск прерван: превышено время ожидания. Пожалуйста, попробуйте позже.",
"searchSuccess": "Трек добавлен в очередь.",
"queueTitle": "Следующий в очереди:",
"historyTitle": "История очереди:",
"viewTitle": "Текущая очередь",
"viewDesc": "**Сейчас играет: [*ссылка*]({0}) ⮯**\n{1}",
"viewFooter": "Страница: {0}/{1} | Общая продолжительность: {2}",
"pauseError": "Плеер уже на паузе.",
"pauseVote": "Пользователь `{0}` проголосовал за паузу плеера. [{1}/{2}]",
"paused": "Пользователь `{0}` поставил плеер на паузу.",
@@ -152,13 +142,10 @@
"skipError": "Нет треков для пропуска.",
"skipVote": "Пользователь `{0}` проголосовал за пропуск трека. [{1}/{2}]",
"skipped": "Пользователь `{0}` пропустил трек.",
"backVote": "Пользователь `{0}` проголосовал за возврат к предыдущему треку. [{1}/{2}]",
"backed": "Пользователь `{0}` вернулся к предыдущему треку.",
"leaveVote": "Пользователь `{0}` проголосовал за остановку плеера. [{1}/{2}]",
"left": "Пользователь `{0}` остановил плеер.",
"seek": "Перемотать на **{0}**",
"repeat": "Режим повтора `{0}`",
"cleared": "Очищены все треки в `{0}`",
@@ -169,25 +156,20 @@
"swapped": "Треки `{0}` и `{1}` поменяны местами",
"moved": "Трек `{0}` смещён на `{1}`",
"autoplay": "Режим autoplay **{0}**",
"notdj": "Вы не DJ. Текущий DJ: {0}.",
"djToMe": "Вы не можете передать роль DJ себе или боту.",
"djnotinchannel": "Пользователь `{0}` не находится в голосовом канале.",
"djswap": "Вы передали роль DJ пользователю `{0}`.",
"chaptersDropdown": "Выберите эпизод для перехода ...",
"noChaptersFound": "Эпизод не найден!",
"chatpersNotSupport": "Эта команда поддерживает только видео на YouTube!",
"voicelinkQueueFull": "Извините, вы достигли максимального количества `{0}` треков в очереди!",
"voicelinkOutofList": "Пожалуйста, предоставьте действительный индекс трека!",
"voicelinkDuplicateTrack": "Извините, этот трек уже есть в очереди.",
"deocdeError": "Что-то пошло не так при декодировании файла!",
"invalidStartTime": "Недействительное значение! Диапозон времени должен быть между `00:00` и `{0}`.",
"invalidEndTime": "Недействительное значение! Диапозон времени должен быть между `00:00` и `{0}`.",
"invalidTimeOrder": "Время конца трека не может быть меньше или равно времени начала.",
"setStageAnnounceTemplate": "Готово! Новые настройки статуса канала скоро применятся в соответсвии с вашим шаблоном.",
"createSongRequestChannel": "Канал для запроса песен ({0}) создан! Вы можете запросить любую песню по названию или URL в этом канале без использования префикса бота."
}
}

View File

@@ -2,7 +2,6 @@
"unknownException": "⚠️ Щось пішло не так під час виконання команди! Будь ласка, спробуйте пізніше або приєднайтеся до нашого сервера Discord для отримання додаткової підтримки.",
"enabled": "вкл",
"disabled": "викл",
"nodeReconnect": "Будь ласка, спробуйте знову! Після перепідключення вузла.",
"noChannel": "Немає голосового каналу для підключення. Будь ласка, вкажіть або приєднайтеся до одного.",
"alreadyConnected": "Уже підключений до голосового каналу.",
@@ -16,13 +15,14 @@
"changedLanguage": "Успішно змінено на мовний пакет `{0}`.",
"setPrefix": "Готово! Мій префікс на вашому сервері тепер `{0}`. Спробуйте запустити `{1}ping`, щоб перевірити його.",
"setDJ": "Встановити роль DJ {0}.",
"setqueue": "Встановити режим черги на `{0}`.",
"setQueue": "Встановити режим черги на `{0}`.",
"247": "Тепер у вас `{0}` режим 24/7.",
"bypassVote": "Тепер у вас `{0}` система голосування.",
"setVolume": "Встановити гучність на `{0}`%",
"togglecontroller": "Тепер у вас `{0}` контролер музики.",
"toggleDuplicateTrack": "Тепер у вас `{0}` запобігання дублюванню треку в черзі.",
"toggleControllerMsg": "Тепер у вас `{0}` повідомлення від контролера музики.",
"toggleSilentMsg": "У вас {0} тихих повідомлень.",
"settingsMenu": "Налаштування сервера | {0}",
"settingsTitle": "❤️ Основна інформація:",
"settingsValue": "```Префікс: {0}\nМова: {1}\nУвімкнути контролер музики: {2}\nDJ роль: {3}\nОбхід голосування: {4}\n24/7: {5}\nГромкість за замовчуванням: {6}%\nЧас програвання: {7}```",
@@ -38,7 +38,6 @@
"addEffect": "Застосуйте ефект `{0}` фільтр.",
"clearEffect": "Звукові ефекти були очищені!",
"FilterTagAlreadyInUse": "Цей звуковий ефект уже використовується! Будь ласка, використовуйте /cleareffect <Тег>, щоб видалити його.",
"playlistViewTitle": "📜 Усі плейлисти користувача {0}",
"playlistViewHeaders": "ID:,Час:,Назва:,Треки:",
"playlistFooter": "Введите /playlist play [плейлист], чтобы добавить плейлист в очередь.",
@@ -77,7 +76,6 @@
"inboxFull": "Вибачте! Поштова скринька {0} переповнена.",
"inboxNoMsg": "У вашій поштовій скриньці немає повідомлень.",
"invitationSent": "Запрошення надіслано {0}.",
"notInChannel": "{0}, для використання голосових команд ви маєте перебувати в {1}. Будь ласка, перезайдіть, якщо ви вже в голосі!",
"noTrackPlaying": "Зараз немає пісень, які грають.",
"noTrackFound": "Пісні з таким запитом не знайдено! Будь ласка, вкажіть дійсне посилання.",
@@ -92,16 +90,13 @@
"lyricsNotFound": "Текст пісні не знайдено. Введіть /lyrics <Назва пісні> <Автор> для пошуку тексту.",
"missingTrackInfo": "Деяка інформація про трек відсутня.",
"noVoiceChannel": "Голосовий канал не знайдено!",
"playlistAddError": "Вам не дозволено додавати потокові відео в плейлист!",
"playlistAddError2": "Сталася помилка під час додавання треків у плейлист!",
"playlistlimited": "Ви досягли ліміту! Ви можете додати тільки {0} пісні до свого плейлиста.",
"playlistrepeated": "Такий самий трек уже є у вашому плейлисті!",
"playlistAdded": "❤️ Додано **{0}** до плейлиста користувача {1} [`{2}`]!",
"playerDropdown": "Виберіть пісню для переходу ...",
"playerFilter": "Виберіть фільтр для застосування ...",
"buttonBack": "Назад",
"buttonPause": "Призупинити",
"buttonResume": "Продовжити",
@@ -117,13 +112,10 @@
"buttonForward": "Вперед",
"buttonRewind": "Назад",
"buttonLyrics": "Тексти пісень",
"nowplayingDesc": "**Зараз грає:**\n```{0}```",
"nowplayingField": "Наступне:",
"nowplayingLink": "Слухати на {0}",
"connect": "Підключено до {0}",
"live": "Прямий ефір",
"playlistLoad": "🎶 Додано плейлист **{0}** з `{1}` піснями в чергу.",
"trackLoad": "Додано **[{0}](<{1}>)** від **{2}** (`{3}`) для початку програвання.\n",
@@ -133,13 +125,11 @@
"searchWait": "Виберіть пісню, яку хочете додати в чергу.",
"searchTimeout": "Пошук перервано: перевищено час очікування. Будь ласка, спробуйте пізніше.",
"searchSuccess": "Пісню додано в чергу.",
"queueTitle": "Наступний у черзі:",
"historyTitle": "Історія черги:",
"viewTitle": "Поточна черга",
"viewDesc": "**Заразом грає: [*посилання*]({0}) ⮯**\n{1}",
"viewFooter": "Сторінка: {0}/{1} | Загальна тривалість: {2}",
"pauseError": "Плеєр уже на паузі.",
"pauseVote": "{0} проголосував за паузу пісні. [{1}/{2}]",
"paused": "`{0}` Поставив плеєр на паузу.",
@@ -152,13 +142,10 @@
"skipError": "Немає пісень для пропуску.",
"skipVote": "{0} проголосував за пропуск пісні. [{1}/{2}]",
"skipped": "`{0}` пропустив пісню.",
"backVote": "{0} проголосував за повернення до попередньої пісні. [{1}/{2}]",
"backed": "`{0}` повернувся до попередньої пісні.",
"leaveVote": "{0} проголосував за зупинку плеєра. [{1}/{2}]",
"left": "`{0}` зупинив плеєр.",
"seek": "Встановити плеєр на **{0}**",
"repeat": "Режим циклу встановлено на `{0}`",
"cleared": "Очищено всі треки в `{0}`",
@@ -169,25 +156,20 @@
"swapped": "Треки `{0}` і `{1}` обміняні місцями",
"moved": "Трек `{0}` зміщений на `{1}`",
"autoplay": "Режим autoplay встановлено на **{0}**",
"notdj": "Ви не DJ. Поточний DJ: {0}.",
"djToMe": "Ви не можете передати роль DJ собі або боту.",
"djnotinchannel": "`{0}` не знаходиться в голосовому каналі.",
"djswap": "Ви передали роль DJ `{0}`.",
"chaptersDropdown": "Виберіть епізод для переходу ...",
"noChaptersFound": "Епізод не знайдено!",
"chatpersNotSupport": "Ця команда підтримує тільки відео з YouTube!",
"voicelinkQueueFull": "Вибачте, ви досягли максимальної кількості `{0}` треків у черзі!",
"voicelinkOutofList": "Будь ласка, надайте дійсний індекс треку!",
"voicelinkDuplicateTrack": "Вибачте, цей трек уже є в черзі.",
"deocdeError": "Щось пішло не так під час декодування файлу!",
"invalidStartTime": "Недійснений час початку! Час має бути в межах `00:00` та `{0}`.",
"invalidEndTime": "Недійснений час закінчення! Час має бути в межах `00:00` та `{0}`.",
"invalidTimeOrder": "Час закінчення не може бути меншим або рівним часу початку.",
"setStageAnnounceTemplate": "Готово! Відтепер статус голосу, як той, в якому ви зараз перебуваєте, буде називатися відповідно до вашого шаблону. Ви повинні побачити оновлення через кілька секунд.",
"createSongRequestChannel": "Канал для запитів пісень ({0}) створено! Ви можете почати запитувати будь-яку пісню за назвою або URL у цьому каналі без необхідності використовувати префікс бота."
}

View File

@@ -5,7 +5,7 @@ RUN groupadd -g 322 lavalink && \
WORKDIR /opt/Lavalink
ADD --chown=lavalink:lavalink https://github.com/lavalink-devs/Lavalink/releases/download/4.0.8/Lavalink.jar .
ADD --chown=lavalink:lavalink https://github.com/lavalink-devs/Lavalink/releases/download/4.1.1/Lavalink.jar .
RUN apt-get update && \
apt-get install -y --no-install-recommends netcat-openbsd && \

View File

@@ -100,9 +100,9 @@ plugins:
recommendationsLoadLimit: 10 # Number of tracks
lavalink:
plugins:
- dependency: "dev.lavalink.youtube:youtube-plugin:1.12.0"
- dependency: "dev.lavalink.youtube:youtube-plugin:1.13.3" # Please check the latest version at https://github.com/lavalink-devs/youtube-source/releases
snapshot: false
- dependency: "com.github.topi314.lavasrc:lavasrc-plugin:4.6.0"
- dependency: "com.github.topi314.lavasrc:lavasrc-plugin:4.7.0" # Please check the latest version at https://github.com/topi314/LavaSrc/releases
snapshot: false
# - dependency: "com.github.example:example-plugin:1.0.0" # required, the coordinates of your plugin
# repository: "https://maven.example.com/releases" # optional, defaults to the Lavalink releases repository by default
@@ -192,4 +192,4 @@ logging:
logback:
rollingpolicy:
max-file-size: 1GB
max-history: 30
max-history: 30

View File

@@ -228,4 +228,4 @@
"247": "247",
"stageannounce": "舞台公告",
"Soundcloud": "Soundcloud"
}
}

View File

@@ -117,11 +117,45 @@
"color": "@@default_embed_color@@"
}
},
"default_buttons": [
["back", "resume", "skip", {"stop": "red"}, "add"],
["tracks"]
],
"disableButtonText": false
"buttons": [
{
"back": {
"emoji": "⏮️",
"label": "@@t_buttonBack@@"
},
"play-pause": {
"states": {
"pause": {
"emoji": "⏸️",
"label": "@@t_buttonPause@@"
},
"resume": {
"emoji": "▶️",
"label": "@@t_buttonResume@@"
}
}
},
"skip": {
"emoji": "⏭️",
"label": "@@t_buttonSkip@@"
},
"stop": {
"emoji": "⏹️",
"label": "@@t_buttonLeave@@",
"style": "red"
},
"add-fav": {
"emoji": "❤️",
"label": ""
}
},
{
"tracks": {
"label": "@@t_playerDropdown@@",
"max_options": 10
}
}
]
},
"default_voice_status_template": "{{@@track_name@@ != 'None' ?? @@track_source_emoji@@ Now Playing: @@track_name@@ // Waiting for song requests}}",
"cooldowns": {

View File

@@ -31,11 +31,10 @@ import subprocess
from io import BytesIO
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
__version__ = "v2.7.1"
__version__ = "v2.7.2b3"
# URLs for update and migration
PYTHON_CMD_NAME = os.path.basename(sys.executable)
print(PYTHON_CMD_NAME)
GITHUB_API_URL = "https://api.github.com/repos/ChocoMeow/Vocard/releases/latest"
VOCARD_URL = "https://github.com/ChocoMeow/Vocard/archive/"
MIGRATION_SCRIPT_URL = f"https://raw.githubusercontent.com/ChocoMeow/Vocard-Magration/main/{__version__}.py"

View File

@@ -29,23 +29,49 @@ import views
import function as func
from discord.ext import commands
from typing import Dict, Type
from typing import Optional, Dict, Type, Union, Any
def key(interaction: discord.Interaction):
return interaction.user
class ControlButton(discord.ui.Button):
def __init__(
self,
player,
label: str = None,
player: "voicelink.Player",
btn_data: Dict[str, Any],
default_states: Optional[str] = None,
**kwargs
):
self.player: voicelink.Player = player
self.disable_button_text: bool = func.settings.controller.get("disableButtonText", False)
super().__init__(label=self.player.get_msg(label) if label and not self.disable_button_text else None, **kwargs)
super().__init__(**kwargs)
self.player: voicelink.Player = player
self.btn_data: Dict[str, Any] = btn_data
self.change_states(default_states)
def _get_button_config(self, states: Optional[str]) -> Dict[str, Any]:
"""Retrieve button configuration based on states."""
if states and "states" in self.btn_data:
return self.btn_data["states"].get(states, {})
return self.btn_data
def _get_button_style(self, style_name: Optional[str]) -> discord.ButtonStyle:
"""Retrieve the corresponding ButtonStyle based on the provided style name."""
if style_name:
for name, btn_style in discord.ButtonStyle.__members__.items():
if name.lower() == style_name.lower():
return btn_style
return discord.ButtonStyle.gray
def change_states(self, states: str) -> None:
"""Change the button's emoji and label based on the provided state."""
states = states.lower() if states else None
state_config = self._get_button_config(states)
if state_config:
self.emoji = state_config.get("emoji") or None
self.style = self._get_button_style(state_config.get("style"))
self.label = self.player._ph.replace(state_config.get("label"), {})
async def send(self, interaction: discord.Interaction, key: str, *params, view: discord.ui.View = None, ephemeral: bool = False) -> None:
stay = self.player.settings.get("controller_msg", True)
return await func.send(
@@ -58,8 +84,6 @@ class ControlButton(discord.ui.Button):
class Back(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="⏮️",
label="buttonBack",
disabled=False if kwargs["player"].queue.history() or not kwargs["player"].current else True,
**kwargs
)
@@ -87,21 +111,20 @@ class Back(ControlButton):
if self.player.queue._repeat.mode == voicelink.LoopType.TRACK:
await self.player.set_repeat(voicelink.LoopType.OFF)
class Resume(ControlButton):
class PlayPause(ControlButton):
def __init__(self, **kwargs):
self.playing_status = lambda player, reverse=False: "pause" if (player.is_paused and not reverse) or (not player.is_paused and reverse) else "resume"
super().__init__(
emoji="⏸️",
label="buttonPause",
default_states="pause",
disabled=kwargs["player"].current is None,
**kwargs
)
async def callback(self, interaction: discord.Interaction):
is_paused = not self.player.is_paused
vote_type = "pause" if is_paused else "resume"
vote_type = self.playing_status(self.player, True)
votes = getattr(self.player, f"{vote_type}_votes")
emoji = "▶️" if is_paused else "⏸️"
button = "buttonResume" if is_paused else "buttonPause"
if not self.player.is_privileged(interaction.user):
if interaction.user in votes:
@@ -111,19 +134,13 @@ class Resume(ControlButton):
if len(votes) < (required := self.player.required()):
return await self.send(interaction, f"{vote_type}Vote", interaction.user, len(votes), required)
self.emoji = emoji
if not self.disable_button_text:
self.label = await func.get_lang(interaction.guild.id, button)
self.change_states(self.playing_status(self.player))
await self.player.set_pause(is_paused, interaction.user)
await interaction.response.edit_message(view=self.view)
class Skip(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="⏭️",
label="buttonSkip",
**kwargs
)
super().__init__(**kwargs)
async def callback(self, interaction: discord.Interaction):
if not self.player.is_playing:
@@ -148,11 +165,7 @@ class Skip(ControlButton):
class Stop(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="⏹️",
label="buttonLeave",
**kwargs
)
super().__init__(**kwargs)
async def callback(self, interaction: discord.Interaction):
if not self.player.is_privileged(interaction.user):
@@ -168,13 +181,9 @@ class Stop(ControlButton):
await self.send(interaction, "left", interaction.user)
await self.player.teardown()
class Add(ControlButton):
class AddFav(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="❤️",
disabled=kwargs["player"].current is None,
**kwargs
)
super().__init__(disabled=kwargs["player"].current is None, **kwargs)
async def callback(self, interaction: discord.Interaction):
track = self.player.current
@@ -197,42 +206,23 @@ class Add(ControlButton):
class Loop(ControlButton):
def __init__(self, **kwargs):
self.btn_emojis: dict[str, str] = {
"off": "🔁",
"track": "🔂",
"queue": "🔁"
}
super().__init__(
emoji=self.get_next_loop_emoji(kwargs["player"]),
label="buttonLoop",
default_states=kwargs["player"].queue._repeat.peek_next().name,
**kwargs
)
def get_next_loop_emoji(self, player) -> str:
current_repeat_mode = player.queue.repeat.lower()
if current_repeat_mode not in self.btn_emojis:
raise ValueError(f"Invalid repeat mode: {current_repeat_mode}")
return self.btn_emojis[current_repeat_mode]
async def callback(self, interaction: discord.Interaction):
if not self.player.is_privileged(interaction.user):
return await self.send(interaction, 'missingPerms_mode', ephemeral=True)
mode = await self.player.set_repeat(requester=interaction.user)
self.emoji = self.get_next_loop_emoji(self.player)
await self.player.set_repeat(requester=interaction.user)
self.change_states(self.player.queue._repeat.peek_next().name)
await interaction.response.edit_message(view=self.view)
await self.send(interaction, 'repeat', mode.name.capitalize())
class VolumeUp(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="🔊",
label="buttonVolumeUp",
**kwargs
)
super().__init__(**kwargs)
async def callback(self, interaction: discord.Interaction):
if not self.player.is_privileged(interaction.user):
@@ -245,11 +235,7 @@ class VolumeUp(ControlButton):
class VolumeDown(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="🔉",
label="buttonVolumeDown",
**kwargs
)
super().__init__(**kwargs)
async def callback(self, interaction: discord.Interaction):
if not self.player.is_privileged(interaction.user):
@@ -263,8 +249,7 @@ class VolumeDown(ControlButton):
class VolumeMute(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="🔇" if kwargs["player"].volume else "🔈",
label="buttonVolumeMute" if kwargs["player"].volume else "buttonVolumeUnmute",
default_states="muted" if kwargs["player"].volume else "mute",
**kwargs
)
@@ -274,20 +259,13 @@ class VolumeMute(ControlButton):
is_muted = self.player.volume != 0
value = 0 if is_muted else self.player.settings.get("volume", 100)
self.emoji = "🔈" if is_muted else "🔇"
if not self.disable_button_text:
self.label = await func.get_lang(interaction.guild_id, "buttonVolumeUnmute" if is_muted else "buttonVolumeMute")
self.change_states("muted" if value else "mute")
await self.player.set_volume(value, interaction.user)
await interaction.response.edit_message(view=self.view)
class AutoPlay(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="💡",
label="buttonAutoPlay",
**kwargs
)
super().__init__(**kwargs)
async def callback(self, interaction: discord.Interaction):
if not self.player.is_privileged(interaction.user):
@@ -302,11 +280,7 @@ class AutoPlay(ControlButton):
class Shuffle(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="🔀",
label="buttonShuffle",
**kwargs
)
super().__init__(**kwargs)
async def callback(self, interaction: discord.Interaction):
if not self.player.is_privileged(interaction.user):
@@ -325,8 +299,6 @@ class Shuffle(ControlButton):
class Forward(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="",
label="buttonForward",
disabled=kwargs["player"].current is None,
**kwargs
)
@@ -346,8 +318,6 @@ class Forward(ControlButton):
class Rewind(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="",
label="buttonRewind",
disabled=kwargs["player"].current is None,
**kwargs
)
@@ -367,8 +337,6 @@ class Rewind(ControlButton):
class Lyrics(ControlButton):
def __init__(self, **kwargs):
super().__init__(
emoji="📜",
label="buttonLyrics",
disabled=kwargs["player"].current is None,
**kwargs
)
@@ -390,22 +358,25 @@ class Lyrics(ControlButton):
view.response = await self.send(interaction, view.build_embed(), view=view, ephemeral=True)
class Tracks(discord.ui.Select):
def __init__(self, player, style, row):
def __init__(self, player: "voicelink.Player", btn_data, **kwargs):
self.player: voicelink.Player = player
if player.queue.is_empty:
raise ValueError("Player queue is empty, cannot create Tracks row instance.")
options = []
for index, track in enumerate(self.player.queue.tracks(), start=1):
if index > 10:
if index > min(max(btn_data.get("max_options", 10), 1), 25):
break
options.append(discord.SelectOption(label=f"{index}. {track.title[:40]}", description=f"{track.author[:30]} · " + ("Live" if track.is_stream else track.formatted_length), emoji=track.emoji))
super().__init__(
placeholder=self.player.get_msg("playerDropdown"),
placeholder=self.player._ph.replace(btn_data.get("label"), {}),
options=options,
row=row
disabled=player.queue.is_empty,
**kwargs
)
async def callback(self, interaction: discord.Interaction):
if not self.player.is_privileged(interaction.user):
return await func.send(interaction, "missingPerms_function", ephemeral=True)
@@ -417,7 +388,7 @@ class Tracks(discord.ui.Select):
await func.send(interaction, "skipped", interaction.user)
class Effects(discord.ui.Select):
def __init__(self, player, style, row):
def __init__(self, player: "voicelink.Player", btn_data, row):
self.player: voicelink.Player = player
@@ -426,11 +397,11 @@ class Effects(discord.ui.Select):
options.append(discord.SelectOption(label=name.capitalize(), value=name))
super().__init__(
placeholder=self.player.get_msg("playerFilter"),
placeholder=self.player._ph.replace(btn_data.get("label"), {}),
options=options,
row=row
)
async def callback(self, interaction: discord.Interaction):
if not self.player.is_privileged(interaction.user):
return await func.send(interaction, "missingPerms_function", ephemeral=True)
@@ -448,12 +419,12 @@ class Effects(discord.ui.Select):
await self.player.add_filter(selected_filter, requester=interaction.user)
await func.send(interaction, "addEffect", selected_filter.tag)
BUTTON_TYPE: Dict[str, Type[ControlButton]] = {
BUTTON_TYPE: Dict[str, Type[Union[ControlButton, discord.ui.Select]]] = {
"back": Back,
"resume": Resume,
"play-pause": PlayPause,
"skip": Skip,
"stop": Stop,
"add": Add,
"add-fav": AddFav,
"loop": Loop,
"volumeup": VolumeUp,
"volumedown": VolumeDown,
@@ -467,30 +438,22 @@ BUTTON_TYPE: Dict[str, Type[ControlButton]] = {
"effects": Effects
}
BUTTON_COLORS: Dict[str, discord.ButtonStyle] = {
"blue": discord.ButtonStyle.primary,
"grey": discord.ButtonStyle.secondary,
"red": discord.ButtonStyle.danger,
"green": discord.ButtonStyle.success
}
class InteractiveController(discord.ui.View):
def __init__(self, player):
super().__init__(timeout=None)
self.player: voicelink.Player = player
for row, btnRow in enumerate(func.settings.controller.get("default_buttons")):
for btn in btnRow:
color = ""
if isinstance(btn, Dict):
color = list(btn.values())[0]
btn = list(btn.keys())[0]
btnClass = BUTTON_TYPE.get(btn.lower())
style = BUTTON_COLORS.get(color.lower(), BUTTON_COLORS["grey"])
if not btnClass or (self.player.queue.is_empty and btn == "tracks"):
for row_num, btn_row in enumerate(func.settings.controller.get("buttons")):
for btn_name, btn_data in btn_row.items():
btn_class = BUTTON_TYPE.get(btn_name.lower())
if not btn_class:
continue
self.add_item(btnClass(player=player, style=style, row=row))
try:
self.add_item(btn_class(player=player, btn_data=btn_data, row=row_num))
except ValueError:
pass
self.cooldown = commands.CooldownMapping.from_cooldown(2.0, 10.0, key)
async def interaction_check(self, interaction: discord.Interaction):
@@ -510,7 +473,7 @@ class InteractiveController(discord.ui.View):
await func.send(interaction, "notInChannel", interaction.user.mention, self.player.channel.mention, ephemeral=True)
return False
async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item):
async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item) -> None:
if isinstance(error, views.ButtonOnCooldown):
sec = int(error.retry_after)
await interaction.response.send_message(f"You're on cooldown for {sec} second{'' if sec == 1 else 's'}!", ephemeral=True)

View File

@@ -70,6 +70,10 @@ class Placeholders:
"server_invite_link": func.settings.invite_link,
"invite_link": f"https://discord.com/oauth2/authorize?client_id={self.bot.user.id}&permissions=2184260928&scope=bot%20applications.commands"
}
self.regexes = {
"@@t_(.*?)@@": self.translation
}
def get_current(self) -> Track:
return self.player.current if self.player else None
@@ -152,11 +156,14 @@ class Placeholders:
def bot_icon(self) -> str:
return self.bot.user.display_avatar.url if self.player else "https://i.imgur.com/dIFBwU7.png"
def translation(self, text: str) -> str:
return self.player.get_msg(text)
def replace(self, text: str, variables: dict[str, str]) -> str:
if not text or text.isspace(): return
pattern = r"\{\{(.*?)\}\}"
matches: list[str] = re.findall(pattern, text)
matches: list[str] = re.findall(r"\{\{(.*?)\}\}", text)
for match in matches:
parts: list[str] = match.split("??")
@@ -185,42 +192,44 @@ class Placeholders:
except:
text = text.replace("{{" + match + "}}", "")
for regex_pattern, func in self.regexes.items():
text = re.sub(regex_pattern, lambda m: func(m.group(1)), text)
text = re.sub(r'@@(.*?)@@', lambda x: str(variables.get(x.group(1), '')), text)
return text
def build_embed(raw: dict[str, dict], placeholder: Placeholders) -> Embed:
def build_embed(embed_form: dict[str, dict], placeholder: Placeholders) -> Embed:
embed = Embed()
try:
rv = {key: func() if callable(func) else func for key, func in placeholder.variables.items()}
if author := raw.get("author"):
if author := embed_form.get("author"):
embed.set_author(
name = placeholder.replace(author.get("name"), rv),
url = placeholder.replace(author.get("url"), rv),
icon_url = placeholder.replace(author.get("icon_url"), rv)
)
if title := raw.get("title"):
if title := embed_form.get("title"):
embed.title = placeholder.replace(title.get("name"), rv)
embed.url = placeholder.replace(title.get("url"), rv)
if fields := raw.get("fields", []):
if fields := embed_form.get("fields", []):
for f in fields:
embed.add_field(name=placeholder.replace(f.get("name"), rv), value=placeholder.replace(f.get("value", ""), rv), inline=f.get("inline", False))
if footer := raw.get("footer"):
if footer := embed_form.get("footer"):
embed.set_footer(
text = placeholder.replace(footer.get("text"), rv),
icon_url = placeholder.replace(footer.get("icon_url"), rv)
)
if thumbnail := raw.get("thumbnail"):
if thumbnail := embed_form.get("thumbnail"):
embed.set_thumbnail(url = placeholder.replace(thumbnail, rv))
if image := raw.get("image"):
if image := embed_form.get("image"):
embed.set_image(url = placeholder.replace(image, rv))
embed.description = placeholder.replace(raw.get("description"), rv)
embed.color = int(placeholder.replace(raw.get("color"), rv))
embed.description = placeholder.replace(embed_form.get("description"), rv)
embed.color = int(placeholder.replace(embed_form.get("color"), rv))
except:
pass

View File

@@ -51,8 +51,8 @@ from .exceptions import VoicelinkException, FilterInvalidArgument, TrackInvalidP
from .filters import Filter, Filters
from .objects import Track, Playlist
from .pool import Node, NodePool
from .queue import Queue, FairQueue
from .placeholders import Placeholders, build_embed
from .queue import Queue, QUEUE_TYPES
from random import shuffle, choice
async def connect_channel(ctx: Union[commands.Context, Interaction], channel: VoiceChannel = None):
@@ -115,7 +115,10 @@ class Player(VoiceProtocol):
self.settings: dict = settings
self.joinTime: float = round(time.time())
self._volume: int = self.settings.get('volume', 100)
self.queue: Queue = eval(self.settings.get("queueType", "Queue"))(self.settings.get("maxQueue", func.settings.max_queue), self.settings.get("duplicateTrack", True), self.get_msg)
self.queue: Queue = QUEUE_TYPES.get(self.settings.get("queue_type", "queue").lower())(
self.settings.get("max_queue", func.settings.max_queue),
self.settings.get("duplicate_track", True), self.get_msg
)
self._node = NodePool.get_node()
self._current: Optional[Track] = None
@@ -263,7 +266,7 @@ class Player(VoiceProtocol):
If `leave` is True and the channel has three members, the requirement adjusts to 2 votes.
"""
if self.settings.get('votedisable'):
if self.settings.get('disabled_vote'):
return 0
required = ceil((len(self.channel.members) - 1) / 2.5)
@@ -302,9 +305,9 @@ class Player(VoiceProtocol):
def build_embed(self, current_track: Track = None):
"""Builds an embed based on the current track state."""
controller = self.settings.get("default_controller", func.settings.controller).get("embeds", {})
raw = controller.get("active" if current_track else "inactive", {})
embed_form = controller.get("active" if current_track else "inactive", {})
return build_embed(raw, self._ph)
return build_embed(embed_form, self._ph)
async def send(self, method: RequestMethod, query: str = None, data: Union[Dict, str] = {}) -> Dict:
"""Sends an HTTP request to the node with the given method, query, and data."""
@@ -460,11 +463,11 @@ class Player(VoiceProtocol):
# Send a new controller message if none exists
if not self.controller:
self.controller = await self.context.channel.send(embed=embed, view=view)
self.controller = await func.send(self.context, content=embed, view=view)
elif not await self.is_position_fresh():
await self.controller.delete()
self.controller = await self.context.channel.send(embed=embed, view=view)
self.controller = await func.send(self.context, content=embed, view=view)
else:
await self.controller.edit(embed=embed, view=view)
@@ -493,8 +496,8 @@ class Player(VoiceProtocol):
"""Cleans up the player and associated resources."""
try:
await func.update_settings(self.guild.id, {"$set": {
"lastActice": (timeNow := round(time.time())),
"playTime": round(self.settings.get("playTime", 0) + ((timeNow - self.joinTime) / 60), 2)
"last_active": (timeNow := round(time.time())),
"played_time": round(self.settings.get("played_time", 0) + ((timeNow - self.joinTime) / 60), 2)
}})
if self.is_ipc_connected:
@@ -711,7 +714,7 @@ class Player(VoiceProtocol):
await self.send_ws({
"op": "shuffleTrack",
"tracks": [{"trackId": track.track_id, "requesterId": str(track.requester.id)} for track in replacement],
"queueType": queue_type
"queue_type": queue_type
}, requester)
self._logger.debug(f"Player in {self.guild.name}({self.guild.id}) has been shuffled the queue.")
@@ -785,7 +788,7 @@ class Player(VoiceProtocol):
if self.is_ipc_connected:
await self.send_ws({
"op": "clearQueue",
"queueType": queue_type
"queue_type": queue_type
}, requester)
async def remove_filter(self, filter_tag: str, requester: Member = None, fast_apply: bool = False) -> Filters:

View File

@@ -113,7 +113,7 @@ class Node:
self._players: Dict[int, Player] = {}
self._info: Optional[NodeInfo] = None
self.yt_ratelimit: Optional[YTRatelimit] = STRATEGY.get(yt_ratelimit.get("strategy"))(self, yt_ratelimit) if yt_ratelimit else None
self.yt_ratelimit: Optional[YTRatelimit] = STRATEGY.get(yt_ratelimit.get("strategy"))(self, yt_ratelimit) if yt_ratelimit and yt_ratelimit.get("tokens") else None
self._bot.add_listener(self._update_handler, "on_socket_response")
@@ -399,6 +399,9 @@ class Node:
return []
tracks = await self.get_tracks(query=query, requester=self.bot.user)
if not track:
return []
if isinstance(tracks, Playlist):
tracks = tracks.tracks

View File

@@ -38,6 +38,13 @@ class LoopTypeCycle:
self.current = next(self._cycle)
return self.current
def peek_next(self) -> LoopType:
temp_cycle = cycle(LoopType)
while next(temp_cycle) != self.current:
pass
return next(temp_cycle)
def set_mode(self, value: LoopType) -> LoopType:
while next(self._cycle) != value:
pass
@@ -216,3 +223,8 @@ class FairQueue(Queue):
self.put_at_index(lastIndex, item)
return lastIndex
QUEUE_TYPES: Dict[str, Queue] = {
"queue": Queue,
"fairqueue": FairQueue
}