Opera8 commited on
Commit
612d50a
·
verified ·
1 Parent(s): b667d11

Create rubikabot.py

Browse files
Files changed (1) hide show
  1. rubikabot.py +371 -0
rubikabot.py ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import base64
4
+ import json
5
+ import httpx
6
+ import uuid
7
+ import hmac
8
+ import hashlib
9
+ from fastapi import APIRouter, Form, File, UploadFile
10
+ from fastapi.responses import HTMLResponse, JSONResponse
11
+
12
+ router = APIRouter()
13
+
14
+ # توکن ثابت ربات بله شما جهت بازسازی خودکار آدرس‌ها در حافظه موقت (بدون دیتابیس)
15
+ BALE_BOT_TOKEN = os.environ.get("BALE_BOT_TOKEN", "2030030646:atvybsEWn6HNfx7ciG_sM6MY-majP0hEQgE").strip()
16
+
17
+ # کلید امنیتی مشترک برای جلوگیری از هک و دستکاری وضعیت اشتراک در لینک‌ها
18
+ SHARED_SECRET = "AI_ALPHA_SECURE_TOKEN_2026"
19
+
20
+ CONTEXT_TRANSLATIONS = {
21
+ "edit_image": "ویرایش عکس 🪄",
22
+ "voice_change": "تغییر صدا 🎙️",
23
+ "voice_clone": "کلون کردن صدا 👤",
24
+ "animate_image": "متحرک‌سازی تصویر 🎞️",
25
+ "video_image": "ساخت ویدیو با عکس 🖼️",
26
+ "image_analysis": "تحلیل تصویر 🔍",
27
+ "stt": "تبدیل صدا به متن 📝",
28
+ "chat_file": "چت با فایل/عکس 🤖"
29
+ }
30
+
31
+ # پوسته زیبای خطای انقضای لینک به صورت کاملاً فارسی
32
+ EXPIRED_HTML = """
33
+ <!DOCTYPE html>
34
+ <html lang="fa" dir="rtl">
35
+ <head>
36
+ <meta charset="UTF-8">
37
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
38
+ <title>لینک منقضی شده است | هوش مصنوعی آلفا</title>
39
+ <style>
40
+ body { font-family: Tahoma, sans-serif; background-color: #1a1a2e; color: #fff; text-align: center; padding: 20px; margin: 0; }
41
+ .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; }
42
+ h2 { color: #e94560; font-size: 24px; margin-bottom: 20px; font-weight: bold; }
43
+ p { color: #a2a2bd; font-size: 15px; line-height: 1.8; margin-bottom: 30px; }
44
+ .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; }
45
+ .btn:hover { background: #ff5e7e; }
46
+ </style>
47
+ </head>
48
+ <body>
49
+ <div class="container">
50
+ <h2>⚠️ لینک منقضی شده است!</h2>
51
+ <p>متأسفانه این لینک منقضی شده است.<br><br>لطفاً از طریق ربات مجدداً روی دکمه مورد نظر کلیک کنید تا لینک جدید و فعال دریافت کنید.</p>
52
+ <a href="https://rubika.ir/aialpha" class="btn">برگشت به روبیکا 🤖</a>
53
+ </div>
54
+ </body>
55
+ </html>
56
+ """
57
+
58
+ BRIDGE_HTML = """
59
+ <!DOCTYPE html>
60
+ <html lang="fa" dir="rtl">
61
+ <head>
62
+ <meta charset="UTF-8">
63
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
64
+ <title>آپلود فایل | هوش مصنوعی آلفا</title>
65
+ <style>
66
+ body { font-family: Tahoma, sans-serif; background-color: #1a1a2e; color: #fff; text-align: center; padding: 20px; margin: 0; }
67
+ .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); }
68
+ h2 { color: #e94560; font-size: 22px; margin-bottom: 5px; }
69
+ .context-badge { display: inline-block; background: #0f3460; color: #43dde6; padding: 5px 15px; border-radius: 20px; font-size: 13px; font-weight: bold; margin-bottom: 20px; }
70
+ p { color: #a2a2bd; font-size: 14px; margin-bottom: 30px; line-height: 1.6; }
71
+ .upload-area { border: 2px dashed #e94560; padding: 40px 20px; border-radius: 10px; cursor: pointer; transition: 0.3s; display: block;}
72
+ .upload-area:hover { background: rgba(233, 69, 96, 0.1); }
73
+ .upload-area input { display: none; }
74
+ .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;}
75
+ .btn:active { transform: scale(0.95); }
76
+ .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; }
77
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
78
+ #success-msg { display: none; color: #4caf50; margin-top: 20px; font-weight: bold; font-size: 18px; line-height: 1.5; }
79
+ .warning { color: #ffeb3b; font-size: 12px; margin-top: 15px; }
80
+ </style>
81
+ </head>
82
+ <body>
83
+ <div class="container">
84
+ <h2>آپلود سنتر آلفا 🚀</h2>
85
+ <div class="context-badge">بخش: {CONTEXT_NAME}</div>
86
+
87
+ <p>فایل خود را اینجا انتخاب کنید تا مستقیماً و پرسرعت به ربات متصل شود.</p>
88
+
89
+ <form id="uploadForm">
90
+ <input type="hidden" id="file_id" name="file_id" value="{FILE_ID}">
91
+ <input type="hidden" id="bot_b64" name="bot_b64" value="{BOT_B64}">
92
+ <input type="hidden" id="t" name="t" value="{TIMESTAMP}">
93
+ <label class="upload-area" id="drop-area">
94
+ <input type="file" id="fileInput" name="file" required>
95
+ <div id="fileName" style="font-size:16px;">📂 برای انتخاب فایل کلیک کنید</div>
96
+ </label>
97
+ <button type="submit" class="btn" id="submitBtn">آپلود و ارسال</button>
98
+ <div class="loader" id="loader"></div>
99
+ <div class="warning">⏳ این لینک یک‌بار مصرف است و تا ۲ دقیقه اعتبار دارد.</div>
100
+ </form>
101
+
102
+ <div id="success-msg">
103
+ ✅ فایل با موفقیت به ربات ارسال شد!<br><br>
104
+ <span style="font-size:14px; color:#a2a2bd;">لطفاً این صفحه را ببندید و به روبیکا برگردید. ربات در حال پردازش است.</span>
105
+ </div>
106
+ </div>
107
+
108
+ <script>
109
+ const fileInput = document.getElementById('fileInput');
110
+ const fileNameDiv = document.getElementById('fileName');
111
+ const form = document.getElementById('uploadForm');
112
+ const loader = document.getElementById('loader');
113
+ const submitBtn = document.getElementById('submitBtn');
114
+ const successMsg = document.getElementById('success-msg');
115
+
116
+ fileInput.addEventListener('change', function() {
117
+ if(this.files.length > 0) {
118
+ fileNameDiv.innerHTML = "📄 " + this.files[0].name;
119
+ }
120
+ });
121
+
122
+ form.addEventListener('submit', function(e) {
123
+ e.preventDefault();
124
+ if(fileInput.files.length === 0) return alert("لطفا یک فایل انتخاب کنید.");
125
+
126
+ submitBtn.style.display = "none";
127
+ loader.style.display = "block";
128
+
129
+ let formData = new FormData(form);
130
+ fetch('/api/bridge/upload', {
131
+ method: 'POST',
132
+ body: formData
133
+ })
134
+ .then(response => response.json())
135
+ .then(data => {
136
+ loader.style.display = "none";
137
+ if(data.status === "success") {
138
+ form.style.display = "none";
139
+ successMsg.style.display = "block";
140
+ } else {
141
+ alert("خطا در آپلود: " + data.message);
142
+ submitBtn.style.display = "block";
143
+ }
144
+ })
145
+ .catch(error => {
146
+ loader.style.display = "none";
147
+ submitBtn.style.display = "block";
148
+ alert("ارتباط با سرور قطع شد!");
149
+ });
150
+ });
151
+ </script>
152
+ </body>
153
+ </html>
154
+ """
155
+
156
+ VIEWER_HTML = """
157
+ <!DOCTYPE html>
158
+ <html lang="fa" dir="rtl">
159
+ <head>
160
+ <meta charset="UTF-8">
161
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
162
+ <title>{FILE_NAME} | دانلود سنتر آلفا</title>
163
+ <style>
164
+ 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; }
165
+ .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); }
166
+ h2 { color: #e94560; font-size: 18px; margin-bottom: 20px; word-wrap: break-word; line-height: 1.6; }
167
+ .media-box { background: #0f3460; padding: 20px; border-radius: 10px; margin-bottom: 25px; display: flex; justify-content: center; align-items: center; min-height: 150px; }
168
+ img, video { max-width: 100%; max-height: 350px; border-radius: 8px; box-shadow: 0 4px 10px rgba(0,0,0,0.3); }
169
+ audio { width: 100%; }
170
+ .file-icon { font-size: 70px; margin-bottom: 10px; display: block; animation: float 3s ease-in-out infinite; }
171
+ @keyframes float { 0% { transform: translateY(0px); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0px); } }
172
+ .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); }
173
+ .btn-dl:hover { background: #ff5e7e; box-shadow: 0 6px 15px rgba(233, 69, 96, 0.5); }
174
+ .btn-dl:active { transform: scale(0.98); }
175
+ .footer { margin-top: 20px; font-size: 12px; color: #a2a2bd; }
176
+ </style>
177
+ </head>
178
+ <body>
179
+ <div class="container">
180
+ <h2>{FILE_NAME}</h2>
181
+ <div class="media-box">
182
+ {MEDIA_CONTENT}
183
+ </div>
184
+ <a href="{BALE_URL}" download="{FILE_NAME}" class="btn-dl">دانلود مستقیم فایل 📥</a>
185
+ <div class="footer">قدرت گرفته از هسته هوش مصنوعی آلفا 🚀</div>
186
+ </div>
187
+ </body>
188
+ </html>
189
+ """
190
+
191
+ def verify_signature(user_code: str, unique_token: str, ctx: str, t: int, p: str, sig: str) -> bool:
192
+ """بررسی و راستی‌آزمایی امضای دیجیتال برای جلوگیری از دستکاری دستی پارامترها"""
193
+ message = f"{user_code}:{unique_token}:{ctx}:{t}:{p}"
194
+ expected_sig = hmac.new(SHARED_SECRET.encode(), message.encode(), hashlib.sha256).hexdigest()
195
+ return hmac.compare_digest(expected_sig, sig)
196
+
197
+ @router.get("/view/{encoded}")
198
+ async def view_file_page(encoded: str):
199
+ """رمزگشایی ریاضیاتی آدرس بله در رم و نمایش صفحه لودر بدون مصرف هارد رانفلر"""
200
+ try:
201
+ missing_padding = len(encoded) % 4
202
+ if missing_padding:
203
+ encoded += '=' * (4 - missing_padding)
204
+
205
+ data_bytes = base64.urlsafe_b64decode(encoded)
206
+ data = json.loads(data_bytes.decode('utf-8'))
207
+
208
+ bale_url = data.get("u")
209
+ file_name = data.get("n", "file")
210
+ file_type = data.get("t", "File")
211
+
212
+ media_content = ""
213
+ ft_lower = file_type.lower()
214
+ if ft_lower in ["photo", "image"]:
215
+ media_content = f'<img src="{bale_url}" alt="{file_name}">'
216
+ elif ft_lower in ["video"]:
217
+ media_content = f'<video src="{bale_url}" controls></video>'
218
+ elif ft_lower in ["voice", "music", "audio"]:
219
+ media_content = f'<audio src="{bale_url}" controls></audio>'
220
+ else:
221
+ media_content = '<div style="text-align:center;"><span class="file-icon">📄</span><span style="color:#a2a2bd; font-size:14px;">نمایش پیش‌نمایش برای این نوع سند در دسترس نیست.</span></div>'
222
+
223
+ final_html = VIEWER_HTML.replace("{FILE_NAME}", file_name).replace("{MEDIA_CONTENT}", media_content).replace("{BALE_URL}", bale_url)
224
+ return HTMLResponse(content=final_html, status_code=200)
225
+ except:
226
+ return HTMLResponse("<h1>❌ فایل معتبر نیست یا لینک مخدوش شده است.</h1>", status_code=400)
227
+
228
+
229
+ @router.get("/v")
230
+ @router.get("/v/")
231
+ async def view_file_short(t: str = "File", id: str = ""):
232
+ """🟢 لودر اختصاصی بسیار کوتاه بدون مصرف هارد و دیسک رانفلر"""
233
+ try:
234
+ bale_url = f"https://tapi.bale.ai/file/bot{BALE_BOT_TOKEN}/{id}"
235
+
236
+ titles = {
237
+ "photo": "تصویر هوش مصنوعی 🎨",
238
+ "image": "تصویر هوش مصنوعی 🎨",
239
+ "video": "ویدیوی ساخته شده 🎬",
240
+ "voice": "صدای تولید شده 🎙️",
241
+ "music": "موسیقی ساخته شده 🎵",
242
+ "audio": "فایل صوتی 🎧",
243
+ "file": "سند متنی آلفا 📄"
244
+ }
245
+ display_name = titles.get(t.lower(), "فایل خروجی آلفا 📁")
246
+
247
+ media_content = ""
248
+ ft_lower = t.lower()
249
+ if ft_lower in ["photo", "image"]:
250
+ media_content = f'<img src="{bale_url}" alt="{display_name}">'
251
+ elif ft_lower in ["video"]:
252
+ media_content = f'<video src="{bale_url}" controls></video>'
253
+ elif ft_lower in ["voice", "music", "audio"]:
254
+ media_content = f'<audio src="{bale_url}" controls></audio>'
255
+ else:
256
+ media_content = '<div style="text-align:center;"><span class="file-icon">📄</span><span style="color:#a2a2bd; font-size:14px;">فرمت فایل عمومی</span></div>'
257
+
258
+ final_html = VIEWER_HTML.replace("{FILE_NAME}", display_name).replace("{MEDIA_CONTENT}", media_content).replace("{BALE_URL}", bale_url)
259
+ return HTMLResponse(content=final_html, status_code=200)
260
+ except:
261
+ return HTMLResponse("<h1>❌ ارتباط با سرور برقرار نشد.</h1>", status_code=400)
262
+
263
+
264
+ @router.get("/upload/{user_code}/{unique_token}")
265
+ async def upload_web_page(user_code: str, unique_token: str, ctx: str = "unknown", b: str = "", t: int = 0, p: str = "0", sig: str = ""):
266
+ """صفحه‌ای که کاربر باز میکند (همراه با بررسی اعتبار زمانی لینک و لود پویای ادیتور روبیکا)"""
267
+ current_time = int(time.time())
268
+ if t == 0 or (current_time - t) > 120: # منقضی شدن لینک وب پس از ۱۲۰ ثانیه (۲ دقیقه)
269
+ return HTMLResponse(content=EXPIRED_HTML, status_code=200)
270
+
271
+ is_premium_verified = False
272
+ if sig and verify_signature(user_code, unique_token, ctx, t, p, sig):
273
+ if p == "1":
274
+ is_premium_verified = True
275
+
276
+ premium_status_val = "paid" if is_premium_verified else "free"
277
+ file_id = f"{user_code}_{ctx}_{unique_token}"
278
+
279
+ # 🟢 اگر کانتکست مربوط به ویرایش عکس بود، فایل شیک ادیتور را لود کن
280
+ if ctx == "edit_image" and os.path.exists("boteditor.html"):
281
+ with open("boteditor.html", "r", encoding="utf-8") as f:
282
+ boteditor_content = f.read()
283
+ final_html = boteditor_content.replace("{FILE_ID}", file_id).replace("{BOT_B64}", b).replace("{TIMESTAMP}", str(t)).replace("{PREMIUM_STATUS}", premium_status_val)
284
+ return HTMLResponse(content=final_html, status_code=200)
285
+
286
+ # 🟢 جدید: اگر کانتکست مربوط به کلون کردن صدا بود، فایل شیک بات کلون را لود کن
287
+ if ctx == "voice_clone" and os.path.exists("botclone.html"):
288
+ with open("botclone.html", "r", encoding="utf-8") as f:
289
+ botclone_content = f.read()
290
+ final_html = botclone_content.replace("{FILE_ID}", file_id).replace("{BOT_B64}", b).replace("{TIMESTAMP}", str(t)).replace("{PREMIUM_STATUS}", premium_status_val)
291
+ return HTMLResponse(content=final_html, status_code=200)
292
+
293
+ context_name = CONTEXT_TRANSLATIONS.get(ctx, "فایل عمومی 📁")
294
+ html_content = BRIDGE_HTML.replace("{FILE_ID}", file_id).replace("{CONTEXT_NAME}", context_name).replace("{BOT_B64}", b).replace("{TIMESTAMP}", str(t))
295
+ return HTMLResponse(content=html_content, status_code=200)
296
+
297
+ @router.post("/api/bridge/upload")
298
+ async def bridge_upload(
299
+ file_id: str = Form(...),
300
+ bot_b64: str = Form(...),
301
+ t: int = Form(...),
302
+ prompt: str = Form(""), # دریافت خودکار پرامپت (دستور متنی) ویرایش عکس
303
+ file: UploadFile = File(...)
304
+ ):
305
+ """دریافت فایل و پرامپت از کاربر و ارسال مستقیم به سرور هاگینگ فیس"""
306
+ current_time = int(time.time())
307
+ if (current_time - t) > 120: # لغو فرآیند آپلود پس از ۲ دقیقه
308
+ return JSONResponse({"status": "error", "message": "این لینک منقضی شده است. مجدداً از ربات لینک بگیرید."})
309
+
310
+ try:
311
+ bot_url = base64.b64decode(bot_b64).decode('utf-8').strip()
312
+ file_content = await file.read()
313
+ target_url = f"{bot_url}/api/bridge/webhook"
314
+
315
+ async with httpx.AsyncClient(verify=False, timeout=90.0) as client:
316
+ resp = await client.post(
317
+ target_url,
318
+ files={"file": (file.filename, file_content, file.content_type)},
319
+ data={
320
+ "file_id": file_id,
321
+ "prompt": prompt # ارسال پرامپت به وب‌هوک هاگینگ فیس
322
+ }
323
+ )
324
+
325
+ if resp.status_code == 200:
326
+ return JSONResponse({"status": "success"})
327
+ else:
328
+ return JSONResponse({"status": "error", "message": f"خطای ارتباط با هاگینگ فیس: {resp.text[:120]}"})
329
+
330
+ except Exception as e:
331
+ return JSONResponse({"status": "error", "message": f"خطای داخلی رانفلر: {str(e)}"})
332
+
333
+ @router.post("/api/bridge/upload_clone")
334
+ async def bridge_upload_clone(
335
+ file_id: str = Form(...),
336
+ bot_b64: str = Form(...),
337
+ t: int = Form(...),
338
+ file1: UploadFile = File(...), # صدای ورودی کاربر
339
+ file2: UploadFile = File(...) # صدای مدل الگو
340
+ ):
341
+ """🟢 مسیر جدید: دریافتِ همزمان دو فایل صوتی برای کلون صدا و ارسال مستقیم به هاگینگ فیس"""
342
+ current_time = int(time.time())
343
+ if (current_time - t) > 120: # لغو فرآیند آپلود پس از ۲ دقیقه
344
+ return JSONResponse({"status": "error", "message": "این لینک منقضی شده است. مجدداً از ربات لینک بگیرید."})
345
+
346
+ try:
347
+ bot_url = base64.b64decode(bot_b64).decode('utf-8').strip()
348
+
349
+ # خواندن موازی بایت‌های دو فایل
350
+ file1_content = await file1.read()
351
+ file2_content = await file2.read()
352
+
353
+ target_url = f"{bot_url}/api/bridge/webhook_clone"
354
+
355
+ async with httpx.AsyncClient(verify=False, timeout=120.0) as client:
356
+ resp = await client.post(
357
+ target_url,
358
+ files={
359
+ "file1": (file1.filename, file1_content, file1.content_type),
360
+ "file2": (file2.filename, file2_content, file2.content_type)
361
+ },
362
+ data={"file_id": file_id}
363
+ )
364
+
365
+ if resp.status_code == 200:
366
+ return JSONResponse({"status": "success"})
367
+ else:
368
+ return JSONResponse({"status": "error", "message": f"خطای ارتباط با هاگینگ فیس: {resp.text[:120]}"})
369
+
370
+ except Exception as e:
371
+ return JSONResponse({"status": "error", "message": f"خطای داخلی رانفلر: {str(e)}"})