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