v1 milestone

This commit is contained in:
YOLANDO
2026-03-25 11:20:05 +07:00
commit 4ec369c18e
7307 changed files with 873141 additions and 0 deletions

166
bot.py Normal file
View File

@@ -0,0 +1,166 @@
import asyncio
import os
import logging
from aiogram import Bot, Dispatcher, types, BaseMiddleware
from aiogram.filters import Command
from aiogram.types import FSInputFile, InlineKeyboardMarkup, InlineKeyboardButton
from aiogram.client.default import DefaultBotProperties
from aiogram.client.session.aiohttp import AiohttpSession # <-- TAMBAH INI
from dotenv import load_dotenv
from apscheduler.schedulers.asyncio import AsyncIOScheduler
# Import modul lokal yang sudah kita buat
from yt_engine import process_youtube_request
from db_manager import db
from s3_manager import upload_audio, download_audio, delete_audio
# Setup Logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
load_dotenv()
BOT_TOKEN = os.getenv("BOT_TOKEN")
# Pastikan ini mengambil angka ID Telegram kamu
AUTHORIZED_USER_ID = int(os.getenv("AUTHORIZED_USER_ID", 0))
# --- INISIALISASI BOT ---
# Timeout diatur 300 detik (5 menit) agar Telegram tidak memutus koneksi saat mengirim file MP3 yang besar
session = AiohttpSession(timeout=300)
bot = Bot(
token=BOT_TOKEN,
session=session,
default=DefaultBotProperties(parse_mode=None)
)
dp = Dispatcher()
# --- MIDDLEWARE / SECURITY CHECK ---
class SecurityMiddleware(BaseMiddleware):
async def __call__(self, handler, event: types.Message, data: dict):
# Memblokir semua orang kecuali Kamu (Single-User Mode)
if event.from_user.id != AUTHORIZED_USER_ID:
logger.warning(f"Akses ditolak untuk User ID: {event.from_user.id}")
return # Abaikan pesan
return await handler(event, data)
# Daftarkan Satpam ke sistem
dp.message.middleware(SecurityMiddleware())
# --- COMMAND HANDLERS ---
@dp.message(Command("start"))
async def cmd_start(message: types.Message):
await message.answer(
"🎧 **Kantor-Bypass Music Bot Ready!**\n\n"
"Kirim judul lagu atau link YouTube dengan format:\n"
"`/play <judul/link>`\n\n"
"Contoh: `/play Dewa 19 Kangen`"
)
@dp.message(Command("play"))
async def cmd_play(message: types.Message):
query = message.text.replace("/play", "").strip()
if not query:
return await message.answer("⚠️ Masukkan judul lagu. Contoh: `/play Nadin Amizah`")
# Kirim status loading
status_msg = await message.answer("🔍 *Mencari dan memproses...*")
# Gunakan asyncio.create_task agar tidak nge-block queue bot (bisa antre banyak lagu)
asyncio.create_task(process_music_request(query, message.chat.id, status_msg.message_id))
# --- CORE LOGIC: PROCESS MUSIC ---
async def process_music_request(query: str, chat_id: int, status_msg_id: int):
local_file = None # Mencegah error 'UnboundLocalError' jika gagal di awal
try:
# 1. Download dari YouTube
yt_result = await process_youtube_request(query)
if yt_result["status"] == "error":
return await bot.edit_message_text(f"❌ Gagal: {yt_result['message']}", chat_id, status_msg_id)
video_id = yt_result["video_id"]
title = yt_result["title"]
local_file = yt_result["file_path"]
s3_object_name = f"{video_id}.mp3"
# 2. Cek Cache di Database
cached_data = await db.get_cache(video_id)
if cached_data:
await bot.edit_message_text("⚡ *Mengambil dari Cache S3...*", chat_id, status_msg_id)
# Download dari S3 ke local temporary
local_file = f"downloads/cache_{video_id}.mp3"
await download_audio(s3_object_name, local_file)
else:
await bot.edit_message_text("☁️ *Mengunggah ke S3 Storage...*", chat_id, status_msg_id)
# Upload file baru ke S3
if local_file and os.path.exists(local_file):
await upload_audio(local_file, s3_object_name)
await db.save_cache(video_id, title, s3_object_name)
# 3. Kirim Audio ke Telegram
await bot.edit_message_text("📤 *Mengirim audio ke Telegram... (Mungkin butuh waktu)*", chat_id, status_msg_id)
if local_file and os.path.exists(local_file):
audio = FSInputFile(local_file)
# Opsi: Tombol pencarian selanjutnya
kb = [[InlineKeyboardButton(text="🎵 Putar Lagu Acak Lainnya", switch_inline_query_current_chat="")]]
reply_markup = InlineKeyboardMarkup(inline_keyboard=kb)
# Ekstra Pengaman Timeout di Request Level
await bot.send_audio(
chat_id=chat_id,
audio=audio,
title=title,
performer="Music Bot",
reply_markup=reply_markup,
request_timeout=300
)
# Hapus pesan status yang muter-muter
await bot.delete_message(chat_id, status_msg_id)
else:
await bot.edit_message_text("❌ File audio tidak ditemukan di server lokal.", chat_id, status_msg_id)
except Exception as e:
logger.error(f"Error processing music: {e}")
try:
await bot.edit_message_text(f"❌ Terjadi kesalahan internal: {e}", chat_id, status_msg_id)
except:
pass # Abaikan jika pesan sudah terhapus
finally:
# 4. Hapus file temporary di VPS (Hemat Disk!)
if local_file and os.path.exists(local_file):
os.remove(local_file)
# --- BACKGROUND JOB: AUTO CLEANUP (7 HARI) ---
async def cleanup_expired_cache():
logger.info("🧹 Menjalankan tugas pembersihan cache otomatis...")
expired_items = await db.get_expired_cache(days=7)
for item in expired_items:
# Hapus dari S3
await delete_audio(item['s3_object_key'])
# Hapus dari Database
await db.delete_cache(item['youtube_id'])
logger.info(f"🗑️ Dihapus: {item['title']} (Usia > 7 Hari)")
# --- MAIN LOOP ---
async def main():
# Konek ke database
await db.connect()
# Jalankan Scheduler untuk bersih-bersih tiap jam 3 pagi
scheduler = AsyncIOScheduler()
scheduler.add_job(cleanup_expired_cache, 'cron', hour=3, minute=0)
scheduler.start()
logger.info("🚀 Bot Music Started!")
# Hapus webhook lama (jika ada) dan mulai polling
await bot.delete_webhook(drop_pending_updates=True)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())