runflare / rubikabot.py
Opera8's picture
Create rubikabot.py
612d50a verified
Raw
History Blame Contribute Delete
19.7 kB
import os
import time
import base64
import json
import httpx
import uuid
import hmac
import hashlib
from fastapi import APIRouter, Form, File, UploadFile
from fastapi.responses import HTMLResponse, JSONResponse
router = APIRouter()
# توکن ثابت ربات بله شما جهت بازسازی خودکار آدرس‌ها در حافظه موقت (بدون دیتابیس)
BALE_BOT_TOKEN = os.environ.get("BALE_BOT_TOKEN", "2030030646:atvybsEWn6HNfx7ciG_sM6MY-majP0hEQgE").strip()
# کلید امنیتی مشترک برای جلوگیری از هک و دستکاری وضعیت اشتراک در لینک‌ها
SHARED_SECRET = "AI_ALPHA_SECURE_TOKEN_2026"
CONTEXT_TRANSLATIONS = {
"edit_image": "ویرایش عکس 🪄",
"voice_change": "تغییر صدا 🎙️",
"voice_clone": "کلون کردن صدا 👤",
"animate_image": "متحرک‌سازی تصویر 🎞️",
"video_image": "ساخت ویدیو با عکس 🖼️",
"image_analysis": "تحلیل تصویر 🔍",
"stt": "تبدیل صدا به متن 📝",
"chat_file": "چت با فایل/عکس 🤖"
}
# پوسته زیبای خطای انقضای لینک به صورت کاملاً فارسی
EXPIRED_HTML = """
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>لینک منقضی شده است | هوش مصنوعی آلفا</title>
<style>
body { font-family: Tahoma, sans-serif; background-color: #1a1a2e; color: #fff; text-align: center; padding: 20px; margin: 0; }
.container { background: #16213e; max-width: 400px; margin: 80px auto; padding: 45px 30px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.5); border: 2.5px solid #e94560; }
h2 { color: #e94560; font-size: 24px; margin-bottom: 20px; font-weight: bold; }
p { color: #a2a2bd; font-size: 15px; line-height: 1.8; margin-bottom: 30px; }
.btn { display: inline-block; background: #e94560; color: #fff; text-decoration: none; padding: 12px 25px; border-radius: 8px; font-size: 16px; font-weight: bold; transition: 0.3s; }
.btn:hover { background: #ff5e7e; }
</style>
</head>
<body>
<div class="container">
<h2>⚠️ لینک منقضی شده است!</h2>
<p>متأسفانه این لینک منقضی شده است.<br><br>لطفاً از طریق ربات مجدداً روی دکمه مورد نظر کلیک کنید تا لینک جدید و فعال دریافت کنید.</p>
<a href="https://rubika.ir/aialpha" class="btn">برگشت به روبیکا 🤖</a>
</div>
</body>
</html>
"""
BRIDGE_HTML = """
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>آپلود فایل | هوش مصنوعی آلفا</title>
<style>
body { font-family: Tahoma, sans-serif; background-color: #1a1a2e; color: #fff; text-align: center; padding: 20px; margin: 0; }
.container { background: #16213e; max-width: 400px; margin: 50px auto; padding: 30px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.5); }
h2 { color: #e94560; font-size: 22px; margin-bottom: 5px; }
.context-badge { display: inline-block; background: #0f3460; color: #43dde6; padding: 5px 15px; border-radius: 20px; font-size: 13px; font-weight: bold; margin-bottom: 20px; }
p { color: #a2a2bd; font-size: 14px; margin-bottom: 30px; line-height: 1.6; }
.upload-area { border: 2px dashed #e94560; padding: 40px 20px; border-radius: 10px; cursor: pointer; transition: 0.3s; display: block;}
.upload-area:hover { background: rgba(233, 69, 96, 0.1); }
.upload-area input { display: none; }
.btn { background: #e94560; color: #fff; border: none; padding: 12px 25px; border-radius: 8px; font-size: 16px; font-weight: bold; cursor: pointer; margin-top: 20px; width: 100%; transition: 0.2s;}
.btn:active { transform: scale(0.95); }
.loader { border: 4px solid #16213e; border-top: 4px solid #e94560; border-radius: 50%; width: 30px; height: 30px; animation: spin 1s linear infinite; margin: 10px auto; display: none; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
#success-msg { display: none; color: #4caf50; margin-top: 20px; font-weight: bold; font-size: 18px; line-height: 1.5; }
.warning { color: #ffeb3b; font-size: 12px; margin-top: 15px; }
</style>
</head>
<body>
<div class="container">
<h2>آپلود سنتر آلفا 🚀</h2>
<div class="context-badge">بخش: {CONTEXT_NAME}</div>
<p>فایل خود را اینجا انتخاب کنید تا مستقیماً و پرسرعت به ربات متصل شود.</p>
<form id="uploadForm">
<input type="hidden" id="file_id" name="file_id" value="{FILE_ID}">
<input type="hidden" id="bot_b64" name="bot_b64" value="{BOT_B64}">
<input type="hidden" id="t" name="t" value="{TIMESTAMP}">
<label class="upload-area" id="drop-area">
<input type="file" id="fileInput" name="file" required>
<div id="fileName" style="font-size:16px;">📂 برای انتخاب فایل کلیک کنید</div>
</label>
<button type="submit" class="btn" id="submitBtn">آپلود و ارسال</button>
<div class="loader" id="loader"></div>
<div class="warning">⏳ این لینک یک‌بار مصرف است و تا ۲ دقیقه اعتبار دارد.</div>
</form>
<div id="success-msg">
✅ فایل با موفقیت به ربات ارسال شد!<br><br>
<span style="font-size:14px; color:#a2a2bd;">لطفاً این صفحه را ببندید و به روبیکا برگردید. ربات در حال پردازش است.</span>
</div>
</div>
<script>
const fileInput = document.getElementById('fileInput');
const fileNameDiv = document.getElementById('fileName');
const form = document.getElementById('uploadForm');
const loader = document.getElementById('loader');
const submitBtn = document.getElementById('submitBtn');
const successMsg = document.getElementById('success-msg');
fileInput.addEventListener('change', function() {
if(this.files.length > 0) {
fileNameDiv.innerHTML = "📄 " + this.files[0].name;
}
});
form.addEventListener('submit', function(e) {
e.preventDefault();
if(fileInput.files.length === 0) return alert("لطفا یک فایل انتخاب کنید.");
submitBtn.style.display = "none";
loader.style.display = "block";
let formData = new FormData(form);
fetch('/api/bridge/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
loader.style.display = "none";
if(data.status === "success") {
form.style.display = "none";
successMsg.style.display = "block";
} else {
alert("خطا در آپلود: " + data.message);
submitBtn.style.display = "block";
}
})
.catch(error => {
loader.style.display = "none";
submitBtn.style.display = "block";
alert("ارتباط با سرور قطع شد!");
});
});
</script>
</body>
</html>
"""
VIEWER_HTML = """
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{FILE_NAME} | دانلود سنتر آلفا</title>
<style>
body { font-family: Tahoma, sans-serif; background-color: #1a1a2e; color: #fff; text-align: center; padding: 20px; margin: 0; display: flex; flex-direction: column; min-height: 100vh; justify-content: center; align-items: center; }
.container { background: #16213e; max-width: 500px; width: 90%; padding: 30px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.5); border: 1px solid rgba(233, 69, 96, 0.2); }
h2 { color: #e94560; font-size: 18px; margin-bottom: 20px; word-wrap: break-word; line-height: 1.6; }
.media-box { background: #0f3460; padding: 20px; border-radius: 10px; margin-bottom: 25px; display: flex; justify-content: center; align-items: center; min-height: 150px; }
img, video { max-width: 100%; max-height: 350px; border-radius: 8px; box-shadow: 0 4px 10px rgba(0,0,0,0.3); }
audio { width: 100%; }
.file-icon { font-size: 70px; margin-bottom: 10px; display: block; animation: float 3s ease-in-out infinite; }
@keyframes float { 0% { transform: translateY(0px); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0px); } }
.btn-dl { display: block; background: #e94560; color: #fff; text-decoration: none; padding: 14px 20px; border-radius: 8px; font-size: 17px; font-weight: bold; transition: 0.3s; box-shadow: 0 4px 10px rgba(233, 69, 96, 0.3); }
.btn-dl:hover { background: #ff5e7e; box-shadow: 0 6px 15px rgba(233, 69, 96, 0.5); }
.btn-dl:active { transform: scale(0.98); }
.footer { margin-top: 20px; font-size: 12px; color: #a2a2bd; }
</style>
</head>
<body>
<div class="container">
<h2>{FILE_NAME}</h2>
<div class="media-box">
{MEDIA_CONTENT}
</div>
<a href="{BALE_URL}" download="{FILE_NAME}" class="btn-dl">دانلود مستقیم فایل 📥</a>
<div class="footer">قدرت گرفته از هسته هوش مصنوعی آلفا 🚀</div>
</div>
</body>
</html>
"""
def verify_signature(user_code: str, unique_token: str, ctx: str, t: int, p: str, sig: str) -> bool:
"""بررسی و راستی‌آزمایی امضای دیجیتال برای جلوگیری از دستکاری دستی پارامترها"""
message = f"{user_code}:{unique_token}:{ctx}:{t}:{p}"
expected_sig = hmac.new(SHARED_SECRET.encode(), message.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(expected_sig, sig)
@router.get("/view/{encoded}")
async def view_file_page(encoded: str):
"""رمزگشایی ریاضیاتی آدرس بله در رم و نمایش صفحه لودر بدون مصرف هارد رانفلر"""
try:
missing_padding = len(encoded) % 4
if missing_padding:
encoded += '=' * (4 - missing_padding)
data_bytes = base64.urlsafe_b64decode(encoded)
data = json.loads(data_bytes.decode('utf-8'))
bale_url = data.get("u")
file_name = data.get("n", "file")
file_type = data.get("t", "File")
media_content = ""
ft_lower = file_type.lower()
if ft_lower in ["photo", "image"]:
media_content = f'<img src="{bale_url}" alt="{file_name}">'
elif ft_lower in ["video"]:
media_content = f'<video src="{bale_url}" controls></video>'
elif ft_lower in ["voice", "music", "audio"]:
media_content = f'<audio src="{bale_url}" controls></audio>'
else:
media_content = '<div style="text-align:center;"><span class="file-icon">📄</span><span style="color:#a2a2bd; font-size:14px;">نمایش پیش‌نمایش برای این نوع سند در دسترس نیست.</span></div>'
final_html = VIEWER_HTML.replace("{FILE_NAME}", file_name).replace("{MEDIA_CONTENT}", media_content).replace("{BALE_URL}", bale_url)
return HTMLResponse(content=final_html, status_code=200)
except:
return HTMLResponse("<h1>❌ فایل معتبر نیست یا لینک مخدوش شده است.</h1>", status_code=400)
@router.get("/v")
@router.get("/v/")
async def view_file_short(t: str = "File", id: str = ""):
"""🟢 لودر اختصاصی بسیار کوتاه بدون مصرف هارد و دیسک رانفلر"""
try:
bale_url = f"https://tapi.bale.ai/file/bot{BALE_BOT_TOKEN}/{id}"
titles = {
"photo": "تصویر هوش مصنوعی 🎨",
"image": "تصویر هوش مصنوعی 🎨",
"video": "ویدیوی ساخته شده 🎬",
"voice": "صدای تولید شده 🎙️",
"music": "موسیقی ساخته شده 🎵",
"audio": "فایل صوتی 🎧",
"file": "سند متنی آلفا 📄"
}
display_name = titles.get(t.lower(), "فایل خروجی آلفا 📁")
media_content = ""
ft_lower = t.lower()
if ft_lower in ["photo", "image"]:
media_content = f'<img src="{bale_url}" alt="{display_name}">'
elif ft_lower in ["video"]:
media_content = f'<video src="{bale_url}" controls></video>'
elif ft_lower in ["voice", "music", "audio"]:
media_content = f'<audio src="{bale_url}" controls></audio>'
else:
media_content = '<div style="text-align:center;"><span class="file-icon">📄</span><span style="color:#a2a2bd; font-size:14px;">فرمت فایل عمومی</span></div>'
final_html = VIEWER_HTML.replace("{FILE_NAME}", display_name).replace("{MEDIA_CONTENT}", media_content).replace("{BALE_URL}", bale_url)
return HTMLResponse(content=final_html, status_code=200)
except:
return HTMLResponse("<h1>❌ ارتباط با سرور برقرار نشد.</h1>", status_code=400)
@router.get("/upload/{user_code}/{unique_token}")
async def upload_web_page(user_code: str, unique_token: str, ctx: str = "unknown", b: str = "", t: int = 0, p: str = "0", sig: str = ""):
"""صفحه‌ای که کاربر باز میکند (همراه با بررسی اعتبار زمانی لینک و لود پویای ادیتور روبیکا)"""
current_time = int(time.time())
if t == 0 or (current_time - t) > 120: # منقضی شدن لینک وب پس از ۱۲۰ ثانیه (۲ دقیقه)
return HTMLResponse(content=EXPIRED_HTML, status_code=200)
is_premium_verified = False
if sig and verify_signature(user_code, unique_token, ctx, t, p, sig):
if p == "1":
is_premium_verified = True
premium_status_val = "paid" if is_premium_verified else "free"
file_id = f"{user_code}_{ctx}_{unique_token}"
# 🟢 اگر کانتکست مربوط به ویرایش عکس بود، فایل شیک ادیتور را لود کن
if ctx == "edit_image" and os.path.exists("boteditor.html"):
with open("boteditor.html", "r", encoding="utf-8") as f:
boteditor_content = f.read()
final_html = boteditor_content.replace("{FILE_ID}", file_id).replace("{BOT_B64}", b).replace("{TIMESTAMP}", str(t)).replace("{PREMIUM_STATUS}", premium_status_val)
return HTMLResponse(content=final_html, status_code=200)
# 🟢 جدید: اگر کانتکست مربوط به کلون کردن صدا بود، فایل شیک بات کلون را لود کن
if ctx == "voice_clone" and os.path.exists("botclone.html"):
with open("botclone.html", "r", encoding="utf-8") as f:
botclone_content = f.read()
final_html = botclone_content.replace("{FILE_ID}", file_id).replace("{BOT_B64}", b).replace("{TIMESTAMP}", str(t)).replace("{PREMIUM_STATUS}", premium_status_val)
return HTMLResponse(content=final_html, status_code=200)
context_name = CONTEXT_TRANSLATIONS.get(ctx, "فایل عمومی 📁")
html_content = BRIDGE_HTML.replace("{FILE_ID}", file_id).replace("{CONTEXT_NAME}", context_name).replace("{BOT_B64}", b).replace("{TIMESTAMP}", str(t))
return HTMLResponse(content=html_content, status_code=200)
@router.post("/api/bridge/upload")
async def bridge_upload(
file_id: str = Form(...),
bot_b64: str = Form(...),
t: int = Form(...),
prompt: str = Form(""), # دریافت خودکار پرامپت (دستور متنی) ویرایش عکس
file: UploadFile = File(...)
):
"""دریافت فایل و پرامپت از کاربر و ارسال مستقیم به سرور هاگینگ فیس"""
current_time = int(time.time())
if (current_time - t) > 120: # لغو فرآیند آپلود پس از ۲ دقیقه
return JSONResponse({"status": "error", "message": "این لینک منقضی شده است. مجدداً از ربات لینک بگیرید."})
try:
bot_url = base64.b64decode(bot_b64).decode('utf-8').strip()
file_content = await file.read()
target_url = f"{bot_url}/api/bridge/webhook"
async with httpx.AsyncClient(verify=False, timeout=90.0) as client:
resp = await client.post(
target_url,
files={"file": (file.filename, file_content, file.content_type)},
data={
"file_id": file_id,
"prompt": prompt # ارسال پرامپت به وب‌هوک هاگینگ فیس
}
)
if resp.status_code == 200:
return JSONResponse({"status": "success"})
else:
return JSONResponse({"status": "error", "message": f"خطای ارتباط با هاگینگ فیس: {resp.text[:120]}"})
except Exception as e:
return JSONResponse({"status": "error", "message": f"خطای داخلی رانفلر: {str(e)}"})
@router.post("/api/bridge/upload_clone")
async def bridge_upload_clone(
file_id: str = Form(...),
bot_b64: str = Form(...),
t: int = Form(...),
file1: UploadFile = File(...), # صدای ورودی کاربر
file2: UploadFile = File(...) # صدای مدل الگو
):
"""🟢 مسیر جدید: دریافتِ همزمان دو فایل صوتی برای کلون صدا و ارسال مستقیم به هاگینگ فیس"""
current_time = int(time.time())
if (current_time - t) > 120: # لغو فرآیند آپلود پس از ۲ دقیقه
return JSONResponse({"status": "error", "message": "این لینک منقضی شده است. مجدداً از ربات لینک بگیرید."})
try:
bot_url = base64.b64decode(bot_b64).decode('utf-8').strip()
# خواندن موازی بایت‌های دو فایل
file1_content = await file1.read()
file2_content = await file2.read()
target_url = f"{bot_url}/api/bridge/webhook_clone"
async with httpx.AsyncClient(verify=False, timeout=120.0) as client:
resp = await client.post(
target_url,
files={
"file1": (file1.filename, file1_content, file1.content_type),
"file2": (file2.filename, file2_content, file2.content_type)
},
data={"file_id": file_id}
)
if resp.status_code == 200:
return JSONResponse({"status": "success"})
else:
return JSONResponse({"status": "error", "message": f"خطای ارتباط با هاگینگ فیس: {resp.text[:120]}"})
except Exception as e:
return JSONResponse({"status": "error", "message": f"خطای داخلی رانفلر: {str(e)}"})