Update main.py
Browse files
main.py
CHANGED
|
@@ -9,7 +9,7 @@ from flask import Flask, request, Response
|
|
| 9 |
from rubpy.bot import BotClient, filters
|
| 10 |
|
| 11 |
# ====================================================================
|
| 12 |
-
# 🌐 بخش اول:
|
| 13 |
# ====================================================================
|
| 14 |
app = Flask(__name__)
|
| 15 |
|
|
@@ -18,12 +18,13 @@ HF_SADA_URL = "https://ezmarynoori-sada.hf.space"
|
|
| 18 |
|
| 19 |
@app.route('/')
|
| 20 |
def home():
|
| 21 |
-
return "ربات روبیکا و سرور
|
| 22 |
|
| 23 |
-
|
| 24 |
-
@app.route('/proxy/<target>/<path:subpath>', methods=['GET', 'POST'])
|
| 25 |
def proxy_hub(target, subpath):
|
| 26 |
-
|
|
|
|
|
|
|
| 27 |
if target == 'podcast':
|
| 28 |
base_url = HF_PODCAST_URL
|
| 29 |
elif target == 'sada':
|
|
@@ -33,52 +34,56 @@ def proxy_hub(target, subpath):
|
|
| 33 |
|
| 34 |
url = f"{base_url}/{subpath}"
|
| 35 |
|
| 36 |
-
#
|
| 37 |
headers = {
|
|
|
|
|
|
|
| 38 |
'X-App-Auth': request.headers.get('X-App-Auth', ''),
|
| 39 |
-
'X-CSRF-Token': request.headers.get('X-CSRF-Token', '')
|
| 40 |
}
|
| 41 |
|
| 42 |
-
# اگر درخواست JSON بود، هدر Content-Type را تنظیم میکنیم
|
| 43 |
if request.is_json:
|
| 44 |
headers['Content-Type'] = 'application/json'
|
|
|
|
|
|
|
| 45 |
|
| 46 |
# حذف هدرهای خالی
|
| 47 |
headers = {k: v for k, v in headers.items() if v}
|
| 48 |
|
| 49 |
try:
|
| 50 |
if request.method == 'POST':
|
| 51 |
-
# بررسی اینکه آیا فایل صوتی در درخواست آپلود شده است (مثل بخش کلون صدا)
|
| 52 |
if request.files:
|
| 53 |
-
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
else:
|
| 56 |
-
|
| 57 |
-
res = requests.post(url, data=
|
| 58 |
else:
|
| 59 |
-
|
| 60 |
-
res = requests.get(url, params=request.args, headers=headers)
|
| 61 |
|
| 62 |
-
# برگرداندن پاسخ هاگینگ فیس مستقیما به سایت (بدون تغییر)
|
| 63 |
return Response(
|
| 64 |
res.content,
|
| 65 |
status=res.status_code,
|
| 66 |
content_type=res.headers.get('Content-Type', 'application/json')
|
| 67 |
)
|
|
|
|
|
|
|
| 68 |
except Exception as e:
|
| 69 |
-
print(f"Proxy Error: {e}")
|
| 70 |
-
return str(e), 500
|
| 71 |
|
| 72 |
def run_flask():
|
| 73 |
-
# اجرای وب سرور در پسزمینه
|
| 74 |
app.run(host="0.0.0.0", port=7860, threaded=True)
|
| 75 |
|
| 76 |
|
| 77 |
# ====================================================================
|
| 78 |
-
# 🤖 بخش دوم:
|
| 79 |
# ====================================================================
|
| 80 |
-
|
| 81 |
-
# سرورهای تولید صدای ربات
|
| 82 |
WORKER_URLS =[
|
| 83 |
"https://hamed744-ttspro.hf.space/generate",
|
| 84 |
"https://hamed744-ttspro2.hf.space/generate",
|
|
@@ -91,7 +96,6 @@ WORKER_URLS =[
|
|
| 91 |
"https://hamed744-ttspro9.hf.space/generate",
|
| 92 |
]
|
| 93 |
|
| 94 |
-
# لیست گویندگان
|
| 95 |
SPEAKERS = {
|
| 96 |
"1": ("شهاب (مرد)", "Charon"), "2": ("آوا (زن)", "Zephyr"), "3": ("نوید (مرد)", "Achird"),
|
| 97 |
"4": ("آرمان (مرد)", "Zubenelgenubi"), "5": ("مهسا (زن)", "Vindemiatrix"), "6": ("دانا (مرد)", "Rasalgethi"),
|
|
@@ -105,22 +109,13 @@ SPEAKERS = {
|
|
| 105 |
"28": ("مهتاب (زن)", "Kore"), "29": ("سام (مرد)", "Fenrir"), "30": ("لیدا (زن)", "Leda")
|
| 106 |
}
|
| 107 |
|
| 108 |
-
# دیکشنری برای ذخیره موقت متن کاربر
|
| 109 |
user_states = {}
|
| 110 |
|
| 111 |
-
# پردازش و ساخت صدا در پسزمینه (برای ربات)
|
| 112 |
async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
|
| 113 |
try:
|
| 114 |
await client.send_message(chat_id, f"⏳ در حال ساخت صدا با گوینده «{speaker_name}»...\n(لطفاً چند ثانیه صبور باشید)")
|
| 115 |
|
| 116 |
-
payload = {
|
| 117 |
-
"text": user_text,
|
| 118 |
-
"speaker": speaker_id,
|
| 119 |
-
"temperature": 1.5,
|
| 120 |
-
"prompt": "",
|
| 121 |
-
"use_live_model": True
|
| 122 |
-
}
|
| 123 |
-
|
| 124 |
headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
|
| 125 |
audio_bytes = None
|
| 126 |
last_error = "هیچ پاسخی دریافت نشد"
|
|
@@ -129,7 +124,7 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
|
|
| 129 |
random.shuffle(workers)
|
| 130 |
|
| 131 |
async with aiohttp.ClientSession(headers=headers) as session:
|
| 132 |
-
for worker_url in workers[:3]:
|
| 133 |
try:
|
| 134 |
async with session.post(worker_url, json=payload, timeout=60) as response:
|
| 135 |
if response.status == 200:
|
|
@@ -156,20 +151,17 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
|
|
| 156 |
try:
|
| 157 |
await client.send_document(chat_id, file_name)
|
| 158 |
except Exception:
|
| 159 |
-
await client.send_message(chat_id, "✅ صدا ساخته شد اما
|
| 160 |
|
| 161 |
if os.path.exists(file_name):
|
| 162 |
os.remove(file_name)
|
| 163 |
else:
|
| 164 |
-
|
| 165 |
-
await client.send_message(chat_id, error_msg)
|
| 166 |
|
| 167 |
except Exception as e:
|
| 168 |
print(f"خطای پردازش صدا: {e}")
|
| 169 |
traceback.print_exc()
|
| 170 |
|
| 171 |
-
|
| 172 |
-
# راهاندازی ربات روبیکا
|
| 173 |
bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
|
| 174 |
|
| 175 |
if bot_token:
|
|
@@ -178,7 +170,6 @@ if bot_token:
|
|
| 178 |
@bot.on_update(filters.private)
|
| 179 |
async def main_handler(client, update):
|
| 180 |
try:
|
| 181 |
-
# استخراج متن پیام
|
| 182 |
user_text = ""
|
| 183 |
if hasattr(update, "text") and update.text:
|
| 184 |
user_text = update.text
|
|
@@ -187,48 +178,33 @@ if bot_token:
|
|
| 187 |
elif hasattr(update, "new_message") and hasattr(update.new_message, "text") and update.new_message.text:
|
| 188 |
user_text = update.new_message.text
|
| 189 |
|
| 190 |
-
if not user_text:
|
| 191 |
-
return
|
| 192 |
-
|
| 193 |
chat_id = getattr(update, "chat_id", None) or getattr(update, "author_guid", None) or getattr(update, "object_guid", None)
|
| 194 |
-
if not chat_id:
|
| 195 |
-
return
|
| 196 |
|
| 197 |
user_text_str = str(user_text).strip()
|
| 198 |
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
welcome_text = "سلام! 🎙️\nمن ربات هوش مصنوعی تبدیل متن به صدا هستم.\n\nهر متنی که دوست داری رو برام بفرست تا با دهها گوینده مختلف برات بخونمش!"
|
| 202 |
-
await update.reply(welcome_text)
|
| 203 |
return
|
| 204 |
|
| 205 |
-
# بررسی اینکه آیا کاربر در حال انتخاب گوینده است (ارسال عدد)
|
| 206 |
if chat_id in user_states:
|
| 207 |
if user_text_str.isdigit() and user_text_str in SPEAKERS:
|
| 208 |
speaker_name, speaker_id = SPEAKERS[user_text_str]
|
| 209 |
-
saved_text = user_states
|
| 210 |
-
|
| 211 |
-
# پاک کردن متن از حافظه پس از انتخاب گوینده
|
| 212 |
-
del user_states[chat_id]
|
| 213 |
-
|
| 214 |
-
# ارسال پروسه ساخت صدا به پسزمینه
|
| 215 |
asyncio.create_task(process_tts(client, chat_id, saved_text, speaker_id, speaker_name))
|
| 216 |
return
|
| 217 |
elif user_text_str.isdigit():
|
| 218 |
await update.reply("❌ شماره وارد شده نامعتبر است! لطفاً یک عدد بین ۱ تا ۳۰ بفرستید.")
|
| 219 |
return
|
| 220 |
|
| 221 |
-
# اگر پیام عدد نبود، یعنی کاربر یک متن جدید برای تبدیل فرستاده است
|
| 222 |
if len(user_text_str) > 500:
|
| 223 |
await update.reply("⚠️ کاربر گرامی، لطفاً متنی کوتاهتر از ۵۰۰ کاراکتر بفرستید.")
|
| 224 |
return
|
| 225 |
|
| 226 |
-
# ذخیره متن در حافظه
|
| 227 |
user_states[chat_id] = user_text_str
|
| 228 |
|
| 229 |
-
|
| 230 |
-
menu_text = """📝 متن شما با موفقیت ذخیره شد!
|
| 231 |
-
لطفاً فقط **شماره** گوینده مورد نظر خود را از لیست زیر بفرستید (مثلا بفرستید: 1):
|
| 232 |
|
| 233 |
1. شهاب (مرد) | 2. آوا (زن) | 3. نوید (مرد)
|
| 234 |
4. آرمان (مرد) | 5. مهسا (زن) | 6. دانا (مرد)
|
|
@@ -240,24 +216,13 @@ if bot_token:
|
|
| 240 |
22. ترانه (زن) | 23. نیکو (زن) | 24. هستی (زن)
|
| 241 |
25. کامیار (مرد)| 26. کیانوش (مرد)| 27. پویا (مرد)
|
| 242 |
28. مهتاب (زن) | 29. سام (مرد) | 30. لیدا (زن)"""
|
| 243 |
-
|
| 244 |
await update.reply(menu_text)
|
| 245 |
|
| 246 |
except Exception as e:
|
| 247 |
-
print(f"خطای کلی ربات: {e}")
|
| 248 |
traceback.print_exc()
|
| 249 |
-
else:
|
| 250 |
-
print("خطا: توکن RUBIKA_AUTH تنظیم نشده است!")
|
| 251 |
-
|
| 252 |
|
| 253 |
-
# ====================================================================
|
| 254 |
-
# 🚀 اجرای همزمان Flask و Rubika Bot
|
| 255 |
-
# ====================================================================
|
| 256 |
if __name__ == "__main__":
|
| 257 |
-
# اجرای Flask در یک Thread مجزا
|
| 258 |
threading.Thread(target=run_flask, daemon=True).start()
|
| 259 |
-
|
| 260 |
-
# اجرای ربات روبیکا در Thread اصلی
|
| 261 |
if bot_token:
|
| 262 |
-
print("
|
| 263 |
bot.run()
|
|
|
|
| 9 |
from rubpy.bot import BotClient, filters
|
| 10 |
|
| 11 |
# ====================================================================
|
| 12 |
+
# 🌐 بخش اول: وبسرور تونل (Proxy) برای دور زدن قطعی اینترنت سایت
|
| 13 |
# ====================================================================
|
| 14 |
app = Flask(__name__)
|
| 15 |
|
|
|
|
| 18 |
|
| 19 |
@app.route('/')
|
| 20 |
def home():
|
| 21 |
+
return "ربات روبیکا و سرور تونل Ai Sada فعال است! 🚀"
|
| 22 |
|
| 23 |
+
@app.route('/proxy/<target>/<path:subpath>', methods=['GET', 'POST', 'OPTIONS'])
|
|
|
|
| 24 |
def proxy_hub(target, subpath):
|
| 25 |
+
if request.method == 'OPTIONS':
|
| 26 |
+
return Response("", status=200)
|
| 27 |
+
|
| 28 |
if target == 'podcast':
|
| 29 |
base_url = HF_PODCAST_URL
|
| 30 |
elif target == 'sada':
|
|
|
|
| 34 |
|
| 35 |
url = f"{base_url}/{subpath}"
|
| 36 |
|
| 37 |
+
# هدرهای دور زدن فیلترینگ و کلودفلر
|
| 38 |
headers = {
|
| 39 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
| 40 |
+
'Accept': '*/*',
|
| 41 |
'X-App-Auth': request.headers.get('X-App-Auth', ''),
|
| 42 |
+
'X-CSRF-Token': request.headers.get('X-CSRF-Token', '')
|
| 43 |
}
|
| 44 |
|
|
|
|
| 45 |
if request.is_json:
|
| 46 |
headers['Content-Type'] = 'application/json'
|
| 47 |
+
elif request.content_type and not request.files:
|
| 48 |
+
headers['Content-Type'] = request.content_type
|
| 49 |
|
| 50 |
# حذف هدرهای خالی
|
| 51 |
headers = {k: v for k, v in headers.items() if v}
|
| 52 |
|
| 53 |
try:
|
| 54 |
if request.method == 'POST':
|
|
|
|
| 55 |
if request.files:
|
| 56 |
+
# تبدیل امن فایلهای صوتی برای آپلود بدون قطعی استریم
|
| 57 |
+
files_dict = {}
|
| 58 |
+
for k, v in request.files.items():
|
| 59 |
+
files_dict[k] = (v.filename, v.read(), v.content_type)
|
| 60 |
+
|
| 61 |
+
form_data = dict(request.form)
|
| 62 |
+
res = requests.post(url, data=form_data, files=files_dict, headers=headers, timeout=120)
|
| 63 |
else:
|
| 64 |
+
data = request.get_data()
|
| 65 |
+
res = requests.post(url, data=data, headers=headers, timeout=120)
|
| 66 |
else:
|
| 67 |
+
res = requests.get(url, params=request.args, headers=headers, timeout=120)
|
|
|
|
| 68 |
|
|
|
|
| 69 |
return Response(
|
| 70 |
res.content,
|
| 71 |
status=res.status_code,
|
| 72 |
content_type=res.headers.get('Content-Type', 'application/json')
|
| 73 |
)
|
| 74 |
+
except requests.exceptions.Timeout:
|
| 75 |
+
return Response('{"message": "ارتباط با سرور هوش مصنوعی قطع شد (تایماوت)"}', status=504, mimetype='application/json')
|
| 76 |
except Exception as e:
|
| 77 |
+
print(f"Proxy Error: {str(e)}")
|
| 78 |
+
return Response(f'{{"message": "ارور تونل پایتون: {str(e)}"}}', status=500, mimetype='application/json')
|
| 79 |
|
| 80 |
def run_flask():
|
|
|
|
| 81 |
app.run(host="0.0.0.0", port=7860, threaded=True)
|
| 82 |
|
| 83 |
|
| 84 |
# ====================================================================
|
| 85 |
+
# 🤖 بخش دوم: ربات روبیکا (تولید صدای مستقیم)
|
| 86 |
# ====================================================================
|
|
|
|
|
|
|
| 87 |
WORKER_URLS =[
|
| 88 |
"https://hamed744-ttspro.hf.space/generate",
|
| 89 |
"https://hamed744-ttspro2.hf.space/generate",
|
|
|
|
| 96 |
"https://hamed744-ttspro9.hf.space/generate",
|
| 97 |
]
|
| 98 |
|
|
|
|
| 99 |
SPEAKERS = {
|
| 100 |
"1": ("شهاب (مرد)", "Charon"), "2": ("آوا (زن)", "Zephyr"), "3": ("نوید (مرد)", "Achird"),
|
| 101 |
"4": ("آرمان (مرد)", "Zubenelgenubi"), "5": ("مهسا (زن)", "Vindemiatrix"), "6": ("دانا (مرد)", "Rasalgethi"),
|
|
|
|
| 109 |
"28": ("مهتاب (زن)", "Kore"), "29": ("سام (مرد)", "Fenrir"), "30": ("لیدا (زن)", "Leda")
|
| 110 |
}
|
| 111 |
|
|
|
|
| 112 |
user_states = {}
|
| 113 |
|
|
|
|
| 114 |
async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
|
| 115 |
try:
|
| 116 |
await client.send_message(chat_id, f"⏳ در حال ساخت صدا با گوینده «{speaker_name}»...\n(لطفاً چند ثانیه صبور باشید)")
|
| 117 |
|
| 118 |
+
payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
|
| 120 |
audio_bytes = None
|
| 121 |
last_error = "هیچ پاسخی دریافت نشد"
|
|
|
|
| 124 |
random.shuffle(workers)
|
| 125 |
|
| 126 |
async with aiohttp.ClientSession(headers=headers) as session:
|
| 127 |
+
for worker_url in workers[:3]:
|
| 128 |
try:
|
| 129 |
async with session.post(worker_url, json=payload, timeout=60) as response:
|
| 130 |
if response.status == 200:
|
|
|
|
| 151 |
try:
|
| 152 |
await client.send_document(chat_id, file_name)
|
| 153 |
except Exception:
|
| 154 |
+
await client.send_message(chat_id, "✅ صدا ساخته شد اما محدودیت ارسال فایل در روبیکا وجود دارد.")
|
| 155 |
|
| 156 |
if os.path.exists(file_name):
|
| 157 |
os.remove(file_name)
|
| 158 |
else:
|
| 159 |
+
await client.send_message(chat_id, f"❌ خطای سرور:\n{last_error}\n\nلطفا چند ثانیه دیگر دوباره تلاش کنید.")
|
|
|
|
| 160 |
|
| 161 |
except Exception as e:
|
| 162 |
print(f"خطای پردازش صدا: {e}")
|
| 163 |
traceback.print_exc()
|
| 164 |
|
|
|
|
|
|
|
| 165 |
bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
|
| 166 |
|
| 167 |
if bot_token:
|
|
|
|
| 170 |
@bot.on_update(filters.private)
|
| 171 |
async def main_handler(client, update):
|
| 172 |
try:
|
|
|
|
| 173 |
user_text = ""
|
| 174 |
if hasattr(update, "text") and update.text:
|
| 175 |
user_text = update.text
|
|
|
|
| 178 |
elif hasattr(update, "new_message") and hasattr(update.new_message, "text") and update.new_message.text:
|
| 179 |
user_text = update.new_message.text
|
| 180 |
|
| 181 |
+
if not user_text: return
|
|
|
|
|
|
|
| 182 |
chat_id = getattr(update, "chat_id", None) or getattr(update, "author_guid", None) or getattr(update, "object_guid", None)
|
| 183 |
+
if not chat_id: return
|
|
|
|
| 184 |
|
| 185 |
user_text_str = str(user_text).strip()
|
| 186 |
|
| 187 |
+
if user_text_str in ["/start", "سلام"]:
|
| 188 |
+
await update.reply("سلام! 🎙️\nمن ربات هوش مصنوعی تبدیل متن به صدا هستم.\nهر متنی دوست داری بفرست تا با دهها گوینده مختلف برات بخونمش!")
|
|
|
|
|
|
|
| 189 |
return
|
| 190 |
|
|
|
|
| 191 |
if chat_id in user_states:
|
| 192 |
if user_text_str.isdigit() and user_text_str in SPEAKERS:
|
| 193 |
speaker_name, speaker_id = SPEAKERS[user_text_str]
|
| 194 |
+
saved_text = user_states.pop(chat_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
asyncio.create_task(process_tts(client, chat_id, saved_text, speaker_id, speaker_name))
|
| 196 |
return
|
| 197 |
elif user_text_str.isdigit():
|
| 198 |
await update.reply("❌ شماره وارد شده نامعتبر است! لطفاً یک عدد بین ۱ تا ۳۰ بفرستید.")
|
| 199 |
return
|
| 200 |
|
|
|
|
| 201 |
if len(user_text_str) > 500:
|
| 202 |
await update.reply("⚠️ کاربر گرامی، لطفاً متنی کوتاهتر از ۵۰۰ کاراکتر بفرستید.")
|
| 203 |
return
|
| 204 |
|
|
|
|
| 205 |
user_states[chat_id] = user_text_str
|
| 206 |
|
| 207 |
+
menu_text = """📝 متن ذخیره شد! فقط **شماره** گوینده را از لیست زیر بفرستید:
|
|
|
|
|
|
|
| 208 |
|
| 209 |
1. شهاب (مرد) | 2. آوا (زن) | 3. نوید (مرد)
|
| 210 |
4. آرمان (مرد) | 5. مهسا (زن) | 6. دانا (مرد)
|
|
|
|
| 216 |
22. ترانه (زن) | 23. نیکو (زن) | 24. هستی (زن)
|
| 217 |
25. کامیار (مرد)| 26. کیانوش (مرد)| 27. پویا (مرد)
|
| 218 |
28. مهتاب (زن) | 29. سام (مرد) | 30. لیدا (زن)"""
|
|
|
|
| 219 |
await update.reply(menu_text)
|
| 220 |
|
| 221 |
except Exception as e:
|
|
|
|
| 222 |
traceback.print_exc()
|
|
|
|
|
|
|
|
|
|
| 223 |
|
|
|
|
|
|
|
|
|
|
| 224 |
if __name__ == "__main__":
|
|
|
|
| 225 |
threading.Thread(target=run_flask, daemon=True).start()
|
|
|
|
|
|
|
| 226 |
if bot_token:
|
| 227 |
+
print("ربات روبیکا و سرور تونل روشن شد...")
|
| 228 |
bot.run()
|