From 80ac57e9e827d4e5a4bf278bc18a316947f540bd Mon Sep 17 00:00:00 2001 From: murprite Date: Tue, 10 Mar 2026 00:15:05 +0700 Subject: [PATCH] Fixed queue, skip, adding tracks --- .idea/Bot.iml | 2 +- .idea/misc.xml | 2 +- Dockerfile | 3 +- bot/cogs/music.py | 127 +++++++++++++++++++++++++++++++++++----------- pyproject.toml | 4 +- 5 files changed, 101 insertions(+), 37 deletions(-) diff --git a/.idea/Bot.iml b/.idea/Bot.iml index e4ab5b7..7e5e305 100644 --- a/.idea/Bot.iml +++ b/.idea/Bot.iml @@ -5,7 +5,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index cc37e7f..73b14f2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index bfb8ab8..3a40e8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,6 @@ RUN apt-get update \ nodejs \ npm \ build-essential \ - curl \ && rm -rf /var/lib/apt/lists/* RUN pip install --upgrade pip @@ -34,8 +33,10 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends \ ffmpeg \ nodejs \ + npm \ && rm -rf /var/lib/apt/lists/* +RUN npm install -g deno # Копируем python пакеты COPY --from=builder /usr/local/lib/python3.13 /usr/local/lib/python3.13 COPY --from=builder /usr/local/bin /usr/local/bin diff --git a/bot/cogs/music.py b/bot/cogs/music.py index 87518e2..96fb6f4 100644 --- a/bot/cogs/music.py +++ b/bot/cogs/music.py @@ -2,28 +2,27 @@ from __future__ import annotations import asyncio from typing import Dict, List, Any, Optional +from middleware import logger import discord from discord.ext import commands import yt_dlp +COG_TYPE="Music" + # yt-dlp конфиг с поддержкой поиска и node YTDL_OPTIONS: Dict[str, Any] = { "format": "bestaudio/best", "noplaylist": True, "quiet": True, - "default_search": "ytsearch1", + "default_search": "ytsearch", "source_address": "0.0.0.0", - "js_runtimes": { - "node": {"path": "/usr/bin/node"} - }, "remote_components": { "ejs:github": "github" }, - "extractor_args": { - "youtube": { - "player_client": ["web_music"] - } - } + # "js_runtimes": { + # "deno": {'path': "/usr/local/bin/deno"} + # } + } FFMPEG_OPTIONS: Dict[str, str] = { @@ -37,10 +36,28 @@ ytdl = yt_dlp.YoutubeDL(YTDL_OPTIONS) class Music(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - self.queue: Dict[int, List[str]] = {} # guild_id -> список запросов + self.queue: Dict[int, List[Dict[str, str]]] = {} + logger.info(text="Инициализация Music", log_type="cog") + + @discord.app_commands.command(name="queue", description="Посмотреть очередь") + async def getQueue(self, interaction: discord.Interaction): + safeQueue = "\n".join( + track["title"] + for guild_queue in self.queue.values() + for track in guild_queue + ) or "❌ Пустая очередь" + + logger.info(text=f"Текущая очередь:\n{safeQueue}", log_type="cog") + + await interaction.response.send_message(f"Текущая очередь:\n{safeQueue}") + async def connect_voice(self, interaction: discord.Interaction) -> Optional[discord.VoiceClient]: if not interaction.user.voice or not interaction.user.voice.channel: + logger.warning( + text=f"Юзер не в голосовом канале.\nЮзер: {interaction.user.voice}. Канал: {interaction.user.voice.channel}", + log_type="cog" + ) await interaction.response.send_message("❌ Вы должны быть в голосовом канале.", ephemeral=True) return None @@ -49,60 +66,87 @@ class Music(commands.Cog): try: voice_client = await interaction.user.voice.channel.connect() except Exception as e: + logger.error(text=f"Не удалось подключиться\nОшибка: {e}", log_type="cog") await interaction.response.send_message(f"❌ Не удалось подключиться: {e}", ephemeral=True) return None - return voice_client - async def get_audio(self, query: str) -> Optional[Dict[str, Any]]: + async def get_audio(self, query: str, interaction: discord.Interaction) -> Optional[Dict[str, Any]]: try: + logger.info( + text=f"Поиск по ключевому слову {query}...", + log_type="cog" + ) if query.startswith("http"): - data = ytdl.extract_info(query, download=False) + data = ytdl.extract_info(query, download=False); + logger.info( + text=f"Предоставлена ссылка {query}", + log_type="cog" + ) else: data = ytdl.extract_info(f"ytsearch:{query}", download=False) + if "entries" in data: + title = data["entries"][0]['title'] data = data["entries"][0] + + logger.info( + text=f"Найдено {title}", + log_type="cog" + ) + else: + logger.warning( + text=f"Ничего не найдено", + log_type="cog" + ) return data - except Exception: + except Exception as e: + logger.error( + text=f"Ошибка при получении аудио {e}", + log_type="cog" + ) + await interaction.followup.send(f":x: Ошибка при получении аудио") return None - async def play_next(self, guild_id: int, channel: discord.TextChannel) -> None: + async def play_next(self, guild_id: int, channel: discord.TextChannel): + if guild_id not in self.queue or not self.queue[guild_id]: await channel.send("📭 Очередь пуста.") return - next_query = self.queue[guild_id].pop(0) - # создаем фиктивный interaction для play - class DummyInteraction: - guild = channel.guild - user = channel.guild.me - response = type('Resp', (), {"send_message": lambda self, msg, ephemeral=False: asyncio.create_task(channel.send(msg))})() + track = self.queue[guild_id].pop(0) - await self.play(DummyInteraction(), next_query) + voice_client = channel.guild.voice_client + if not voice_client: + return + + await self.start_track(voice_client, guild_id, channel, track) @discord.app_commands.command(name="play", description="Воспроизвести трек или добавить в очередь") async def play(self, interaction: discord.Interaction, query: str): voice_client = await self.connect_voice(interaction) + if voice_client is None: return guild_id = interaction.guild.id if guild_id not in self.queue: self.queue[guild_id] = [] + await interaction.response.defer() + data = await self.get_audio(query, interaction) - if voice_client.is_playing(): - self.queue[guild_id].append(query) - await interaction.response.send_message("➕ Трек добавлен в очередь.") - return - - data = await self.get_audio(query) if not data: - await interaction.response.send_message("❌ Не удалось найти трек.") + await interaction.followup.send("❌ Не удалось найти трек.") return stream_url = data["url"] title = data.get("title", "Неизвестный трек") + if voice_client.is_playing(): + self.queue[guild_id].append({"title" : title, "stream_url" : stream_url}) + await interaction.followup.send(f"➕ Трек {title} добавлен в очередь.") + return + try: source = discord.FFmpegOpusAudio( stream_url, @@ -110,10 +154,12 @@ class Music(commands.Cog): **FFMPEG_OPTIONS ) except Exception: - await interaction.response.send_message("❌ Ошибка воспроизведения.") + await interaction.followup.send("❌ Ошибка воспроизведения.") return + await interaction.followup.send(f"▶️ Сейчас играет: **{title}**") def after_playing(error): + logger.info(text=f"Включаем следующий трек...", log_type="cog") fut = asyncio.run_coroutine_threadsafe(self.play_next(guild_id, interaction.channel), self.bot.loop) try: fut.result() @@ -121,10 +167,13 @@ class Music(commands.Cog): pass voice_client.play(source, after=after_playing) - await interaction.response.send_message(f"▶️ Сейчас играет: **{title}**") @discord.app_commands.command(name="skip", description="Пропустить текущий трек") async def skip(self, interaction: discord.Interaction): + logger.info( + text=f"Скип...", + log_type="cog" + ) voice_client = interaction.guild.voice_client if not voice_client or not voice_client.is_playing(): await interaction.response.send_message("❌ Сейчас ничего не играет.", ephemeral=True) @@ -149,6 +198,22 @@ class Music(commands.Cog): await voice_client.disconnect() await interaction.response.send_message("👋 Бот вышел из голосового канала.") + async def start_track(self, voice_client, guild_id, channel, track): + source = discord.FFmpegOpusAudio( + track["stream_url"], + executable="ffmpeg", + **FFMPEG_OPTIONS + ) + + await channel.send(f"▶️ Сейчас играет: **{track['title']}**") + + def after_playing(error): + asyncio.run_coroutine_threadsafe( + self.play_next(guild_id, channel), + self.bot.loop + ) + + voice_client.play(source, after=after_playing) async def setup(bot: commands.Bot): await bot.add_cog(Music(bot)) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index a9b25a7..5090eb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,9 +16,7 @@ dependencies = [ "pynacl(>=1.6.2)", "discord(>=2.3.2,<3.0.0)", "davey(>=0.1.4)" - ] - - +] [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"]