diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..dee7dee --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,21 @@ +name: Deploy Music Bot + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: backup-runner # <-- Используем ваш label для доступа к сокету Docker + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Build and Deploy Bot + run: | + # Запускаем сборку и деплой прямо через сокет хоста + docker compose up -d --build + + - name: Clean up old images + run: docker image prune -f \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/auto_dj.iml b/.idea/auto_dj.iml new file mode 100644 index 0000000..c03f621 --- /dev/null +++ b/.idea/auto_dj.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..590a59e --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..054b83e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..286d136 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# Берем легкий образ питона +FROM python:3.11-slim + +# Устанавливаем ffmpeg (он критически нужен yt-dlp для извлечения mp3) +RUN apt-get update && \ + apt-get install -y ffmpeg && \ + rm -rf /var/lib/apt/lists/* + +# Создаем рабочую папку +WORKDIR /app + +# Копируем зависимости и устанавливаем их +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Копируем весь остальной код +COPY . . + +# Запускаем бота +CMD ["python", "bot.py"] \ No newline at end of file diff --git a/devbox.json b/devbox.json new file mode 100644 index 0000000..3165b8b --- /dev/null +++ b/devbox.json @@ -0,0 +1,19 @@ +{ + "packages": [ + "python@3.11", + "ffmpeg@latest" + ], + "env": { + "VENV_DIR": ".venv" + }, + "shell": { + "init_hook": [ + "python -m venv $VENV_DIR", + "source $VENV_DIR/bin/activate", + "pip install -r requirements.txt" + ], + "scripts": { + "start": "python bot.py" + } + } +} \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..e1d55d1 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,12 @@ +services: + music-bot: + build: . + container_name: navidrome_bot + restart: unless-stopped + environment: + - TG_BOT_TOKEN=8849605262:AAGMJWr-02uaFI-cD4UBxg1pSB0swDH_dUc + - ADMIN_ID=812709121 + - MUSIC_DIR=/music + volumes: + # Слева — путь к музыке на сервере, справа — внутри контейнера бота + - /home/redjinald/navidrome/music:/music \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..729c0b8 --- /dev/null +++ b/main.py @@ -0,0 +1,71 @@ +import os +import asyncio +from aiogram import Bot, Dispatcher, types, F +from aiogram.filters import Command +import yt_dlp + +TOKEN = os.getenv("TG_BOT_TOKEN") +MUSIC_DIR = os.getenv("MUSIC_DIR", "./music") +# Читаем ваш ID из переменных среды (по умолчанию 0) +ADMIN_ID = int(os.getenv("ADMIN_ID", 0)) + +bot = Bot(token=TOKEN) +dp = Dispatcher() + +def download_youtube(url: str): + ydl_opts = { + 'format': 'bestaudio/best', + 'postprocessors': [{ + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + 'preferredquality': '192', + }], + 'outtmpl': f'{MUSIC_DIR}/Youtube/%(uploader)s/%(title)s.%(ext)s', + 'noplaylist': False, + 'quiet': False + } + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + ydl.download([url]) + +# Создаем удобный фильтр: "сообщение от админа" +is_admin = F.from_user.id == ADMIN_ID + +# --- ОБРАБОТЧИКИ ТОЛЬКО ДЛЯ ВАС (работают, если is_admin == True) --- + +@dp.message(is_admin, Command("start")) +async def start_cmd(message: types.Message): + await message.reply("Привет, хозяин! Скинь ссылку на YouTube или аудиофайл, и я закину его в Navidrome.") + +@dp.message(is_admin, F.text.regexp(r'https?://(?:www\.)?youtu')) +async def handle_youtube(message: types.Message): + msg = await message.reply("⏳ Начинаю скачивание...") + try: + await asyncio.to_thread(download_youtube, message.text) + await msg.edit_text("✅ Успешно скачано и добавлено в Navidrome!") + except Exception as e: + await msg.edit_text(f"❌ Ошибка:\n{str(e)[:500]}") + +@dp.message(is_admin, F.audio) +async def handle_audio(message: types.Message): + msg = await message.reply("📥 Сохраняю трек...") + try: + file_name = message.audio.file_name or f"track_{message.message_id}.mp3" + destination = os.path.join(MUSIC_DIR, "Telegram", file_name) + os.makedirs(os.path.dirname(destination), exist_ok=True) + await bot.download(message.audio, destination=destination) + await msg.edit_text("✅ Трек добавлен в Navidrome!") + except Exception as e: + await msg.edit_text(f"❌ Ошибка:\n{str(e)[:500]}") + +# --- ОБРАБОТЧИК ДЛЯ ЧУЖАКОВ --- +# Сработает, если ни один из верхних обработчиков не подошел (например, другой ID) +@dp.message() +async def reject_stranger(message: types.Message): + await message.reply(f"⛔ Извините, этот бот приватный.\n\nВаш Telegram ID: {message.from_user.id}", parse_mode="HTML") + +async def main(): + print("Бот запущен...") + await dp.start_polling(bot) + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..385e30e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +aiogram==3.4.1 +yt-dlp==2024.3.10 +mutagen \ No newline at end of file