Opera8 commited on
Commit
b54374c
·
verified ·
1 Parent(s): 39442b0

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +453 -230
main.py CHANGED
@@ -16,36 +16,52 @@ import concurrent.futures
16
  from flask import Flask
17
  from rubpy.bot import BotClient, filters
18
 
19
- # ایمپورت‌های جدید برای هوش مصنوعی، کار با فایل صوتی و دیتابیس هاگینگ فیس
20
  from huggingface_hub import AsyncInferenceClient, HfApi, hf_hub_download
21
  from PIL import Image
22
  from pydub import AudioSegment
23
 
24
- # --- کد مدیریت برای ارتقای کاربران بدون نیاز به لاگین ---
25
- ADMIN_CODE = "3011"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- # متغیر سراسری برای ذخیره آیدی خود ربات (جلوگیری از جواب دادن ربات به خودش)
 
28
  BOT_GUID = None
29
 
30
  # =======================================================
31
- # 🔥 سیستم ضد اسپم کاربر-محور (اصلاح شده) 🔥
32
  # =======================================================
33
  user_message_times = {}
34
 
35
  def is_user_spamming(chat_id):
36
  now = time.time()
37
  times = user_message_times.get(chat_id,[])
38
- # فقط زمان پیام‌های 3 ثانیه اخیر را نگه می‌داریم
39
- times =[t for t in times if now - t < 3.0]
40
  times.append(now)
41
  user_message_times[chat_id] = times
42
-
43
- # اگر یک کاربر به تنهایی در 3 ثانیه بیش از 4 پیام داد، پیامش نادیده گرفته شود
44
  if len(times) > 4:
45
  return True
46
  return False
47
 
48
- # --- سیستم دیتابیس حساب کاربری متصل به دیتاست هاگینگ فیس ---
49
  DB_FILE = "users_db.json"
50
  DATASET_REPO = "opera8/Karbaran-rayegan-tedad"
51
  HF_TOKEN_DB = os.environ.get("HF_TOKEN")
@@ -72,7 +88,16 @@ def gregorian_to_jalali(gy, gm, gd):
72
 
73
  def load_db():
74
  print("در حال تلاش برای خواندن دیتابیس کاربران از دیتاست هاگینگ فیس...")
75
- if HF_TOKEN_DB:
 
 
 
 
 
 
 
 
 
76
  try:
77
  file_path = hf_hub_download(
78
  repo_id=DATASET_REPO,
@@ -84,15 +109,13 @@ def load_db():
84
  print("✅ دیتابیس با موفقیت از دیتاست هاگینگ فیس لود شد.")
85
  return json.load(f)
86
  except Exception as e:
87
- print("⚠️ فایل دیتابیس در هاگینگ فیس یافت نشد یا خطایی رخ داد (ساخت دیتابیس جدید).")
88
-
89
- if os.path.exists(DB_FILE):
90
- try:
91
- with open(DB_FILE, "r", encoding="utf-8") as f:
92
- return json.load(f)
93
- except Exception:
94
- pass
95
- return {}
96
 
97
  db_needs_upload = False
98
 
@@ -101,21 +124,19 @@ def save_db(db_data):
101
  with db_lock:
102
  try:
103
  with open(DB_FILE, "w", encoding="utf-8") as f:
104
- json.dump(db_data, f, ensure_ascii=False, indent=4)
105
- db_needs_upload = True # فقط تیک می‌زنیم که نیاز به آپلود دارد
106
  except Exception as e:
107
  print("خطا در ذخیره دیتابیس:", e)
108
 
109
- # یک تسک پس‌زمینه برای آپلود منظم و بدون فشار به سرور
110
  async def background_db_uploader():
111
  global db_needs_upload
112
  while True:
113
- await asyncio.sleep(300) # هر ۵ دقیقه چک می‌کند
114
  if db_needs_upload and HF_TOKEN_DB:
115
  db_needs_upload = False
116
  api = HfApi(token=HF_TOKEN_DB)
117
  try:
118
- # استفاده از to_thread برای جلوگیری از قفل شدن ربات
119
  await asyncio.to_thread(
120
  api.upload_file,
121
  path_or_fileobj=DB_FILE,
@@ -126,7 +147,7 @@ async def background_db_uploader():
126
  print("✅ بکاپ دیتابیس در هاگینگ فیس ذخیره شد.")
127
  except Exception as e:
128
  print("❌ خطا در آپلود دیتابیس به هاگینگ فیس:", str(e)[:100])
129
- db_needs_upload = True # اگر خطا داد دفعه بعد دوباره تلاش کند
130
 
131
  user_credits_db = load_db()
132
 
@@ -135,7 +156,6 @@ def get_or_create_referral_code(chat_id):
135
  if not user_data.get("referral_code"):
136
  while True:
137
  new_code = ''.join(random.choices(string.digits, k=8))
138
- # در اینجا بررسی میکنیم مقدار دیتابیس حتما دیکشنری (کاربر) باشد و نه لیست پردازش پیام ها
139
  exists = any(isinstance(u, dict) and u.get("referral_code") == new_code for u in user_credits_db.values())
140
  if not exists:
141
  user_data["referral_code"] = new_code
@@ -166,6 +186,9 @@ def get_user_credits(chat_id):
166
  "tts": 5,
167
  "file": 1,
168
  "stt": 5,
 
 
 
169
  "has_joined": False,
170
  "invited_count": 0,
171
  "used_referral": False,
@@ -174,6 +197,11 @@ def get_user_credits(chat_id):
174
  save_db(user_credits_db)
175
 
176
  user_data = user_credits_db[str_chat_id]
 
 
 
 
 
177
  is_premium = user_data.get("is_premium", False)
178
 
179
  if is_premium and user_data.get("expire_date"):
@@ -196,13 +224,14 @@ def get_user_credits(chat_id):
196
  user_data["tts"] = 5
197
  user_data["file"] = 1
198
  user_data["stt"] = 5
 
 
199
  save_db(user_credits_db)
200
 
201
  return user_data
202
 
203
  def to_english_digits(text):
204
- if not text:
205
- return text
206
  persian_digits = '۰۱۲۳۴۵۶۷۸۹'
207
  arabic_digits = '٠١٢٣٤٥٦٧٨٩'
208
  english_digits = '0123456789'
@@ -214,12 +243,12 @@ app = Flask(__name__)
214
 
215
  @app.route('/')
216
  def home():
217
- return "ربات یکپارچه آلفا (نسخه پرو + مدیریت اشتراک نامحدود + دعوت دوستان + سیستم ضد باگ) روشن است! 🚀"
218
 
219
  def run_flask():
220
  app.run(host="0.0.0.0", port=7860, threaded=True)
221
 
222
- # --- توابع کمکی برای جلوگیری از بلاک شدن Event Loop ---
223
  def sync_save_image(image, file_name):
224
  rgb_im = image.convert('RGB')
225
  rgb_im.save(file_name, format="JPEG", quality=100)
@@ -259,6 +288,12 @@ MAIN_KEYPAD_DICT = {
259
  {"id": "tts_btn", "type": "Simple", "button_text": "تبدیل متن به صدا🗣️"}
260
  ]
261
  },
 
 
 
 
 
 
262
  {
263
  "buttons":[
264
  {"id": "stt_btn", "type": "Simple", "button_text": "فایل صوتی به متن 📝"},
@@ -332,78 +367,54 @@ async def check_channel_membership(client, user_id):
332
  elif hasattr(res, 'exist') and hasattr(res.exist, 'chat'):
333
  CHANNEL_GUID = getattr(res.exist.chat, 'object_guid', None)
334
 
335
- if not CHANNEL_GUID:
336
- return True
337
 
338
- payload = {
339
- "channel_guid": CHANNEL_GUID,
340
- "member_guid": user_id
341
- }
342
  res = await client._make_request("getChannelParticipant", payload)
343
 
344
- if isinstance(res, dict):
345
- if res.get("status") == "OK":
346
- data = res.get("data", {})
347
- if data and "participant" in data:
348
- return True
349
- elif hasattr(res, 'status'):
350
- if getattr(res, 'status') == "OK":
351
- return True
352
-
353
- return False
354
- except Exception:
355
  return False
 
356
 
357
  async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
358
  try:
359
- if not use_keyboard:
360
- return await client.send_message(chat_id, text)
361
-
362
- payload = {
363
- "chat_id": chat_id,
364
- "text": text,
365
- "chat_keypad_type": "New",
366
- "chat_keypad": MAIN_KEYPAD_DICT
367
- }
368
  return await client._make_request("sendMessage", payload)
369
  except Exception:
370
- try:
371
- return await client.send_message(chat_id, text)
372
- except Exception:
373
- return None
374
-
375
 
376
  bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
377
 
378
  # ==================================================================
379
- # 🔥 تابع دانلود کاملاً ضد بمب اتم (35 بار پافشاری و کنترل 502)
380
  # ==================================================================
381
  async def helper_download_file(client, msg_obj):
382
  errors =[]
383
  file_obj = None
384
  file_id = None
385
 
386
- # 1. استخراج آبجکت و آیدی فایل با دقت بالا
387
  for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
388
  val = getattr(msg_obj, attr, None)
389
  if val:
390
  file_obj = val
391
- if hasattr(val, 'file_id'):
392
- file_id = val.file_id
393
- elif isinstance(val, dict) and 'file_id' in val:
394
- file_id = val['file_id']
395
  break
396
 
397
  if not file_obj and hasattr(msg_obj, "file_id"):
398
  file_id = msg_obj.file_id
399
  file_obj = msg_obj
400
 
401
- if not file_id:
402
- raise Exception("خطا: هیچ فایلی در پیام یافت نشد.")
403
 
404
  temp_name = f"temp_dl_{uuid.uuid4().hex}.tmp"
405
 
406
- # روش 1: استفاده مستقیم از API قدرتمند روبیکا با پافشاری بسیار بالا (20 بار)
407
  for attempt in range(20):
408
  try:
409
  url_get_file = f"https://botapi.rubika.ir/v3/{bot_token}/getFile"
@@ -417,27 +428,22 @@ async def helper_download_file(client, msg_obj):
417
  if res_data.get("status") == "OK" or res_data.get("status_det") == "OK":
418
  download_url = res_data.get("data", {}).get("download_url") or res_data.get("data", {}).get("file_url")
419
  if download_url:
420
- # اگر لینک داد، 3 بار هم برای خود لینک تلاش میکنیم تا اگر قطع شد جبران شود
421
  for dl_attempt in range(3):
422
  try:
423
  async with session.get(download_url, headers=headers, timeout=60) as dl_resp:
424
  if dl_resp.status == 200:
425
  data = await dl_resp.read()
426
- if data and len(data) > 0:
427
- return data
428
- else:
429
- errors.append(f"DL HTTP {dl_resp.status}")
430
  await asyncio.sleep(2)
431
  except Exception as dl_err:
432
  errors.append(f"DL Error: {str(dl_err)[:30]}")
433
  await asyncio.sleep(2)
434
- else:
435
- errors.append("No download URL in response.")
436
- else:
437
- errors.append(f"API Not OK: {res_data.get('status')}")
438
- elif resp.status in[502, 503, 500]:
439
  errors.append(f"GetFile HTTP {resp.status}")
440
- await asyncio.sleep(3.5) # صبر بیشتر برای خطای 502
441
  continue
442
  else:
443
  errors.append(f"GetFile HTTP {resp.status}")
@@ -446,25 +452,20 @@ async def helper_download_file(client, msg_obj):
446
  errors.append(f"API Error: {str(e)[:50]}")
447
  await asyncio.sleep(2)
448
 
449
- # روش 2: کتابخانه rubpy (بک‌آپ با 10 بار پافشاری)
450
  if file_obj:
451
  for attempt in range(10):
452
  try:
453
  if hasattr(client, "download"):
454
  result = await client.download(file_obj, save_as=temp_name)
455
- if isinstance(result, bytes) and len(result) > 0:
456
- return result
457
  if os.path.exists(temp_name):
458
  data = await asyncio.to_thread(sync_read_file, temp_name)
459
  os.remove(temp_name)
460
- if data and len(data) > 0:
461
- return data
462
  except Exception as e:
463
- err_str = str(e)
464
- errors.append(f"Rubpy Obj Error: {err_str[:50]}")
465
  await asyncio.sleep(3)
466
 
467
- # روش 3: متد قدیمی rubpy (بک‌آپ نهایی با 5 بار پافشاری)
468
  for attempt in range(5):
469
  try:
470
  if hasattr(client, "download_file"):
@@ -472,23 +473,33 @@ async def helper_download_file(client, msg_obj):
472
  if os.path.exists(temp_name):
473
  data = await asyncio.to_thread(sync_read_file, temp_name)
474
  os.remove(temp_name)
475
- if data and len(data) > 0:
476
- return data
477
  except Exception as e:
478
  errors.append(f"Rubpy FileId Error: {str(e)[:50]}")
479
  await asyncio.sleep(3)
480
 
481
  raise Exception(f"سرورهای دانلود روبیکا پس از ۳۵ بار تلاش پاسخ ندادند!\nلاگ خطاها: {str(errors[-5:])}")
482
 
 
 
 
 
 
 
 
 
 
 
483
 
 
 
 
484
  GEMINI_KEYS_STR1 = os.environ.get("GEMINI_API_KEYS1", "")
485
  GEMINI_KEYS_STR2 = os.environ.get("GEMINI_API_KEYS2", "")
486
 
487
  _raw_keys =[]
488
- if GEMINI_KEYS_STR1:
489
- _raw_keys.extend(GEMINI_KEYS_STR1.split(","))
490
- if GEMINI_KEYS_STR2:
491
- _raw_keys.extend(GEMINI_KEYS_STR2.split(","))
492
 
493
  GEMINI_KEYS = list(set([k.strip() for k in _raw_keys if k.strip()]))
494
  print(f"✅ تعداد {len(GEMINI_KEYS)} کلید جیمینای با موفقیت شناسایی شد.")
@@ -500,46 +511,34 @@ def get_next_gemini_keys(count=100):
500
  global current_gemini_key_index
501
  with gemini_key_lock:
502
  total_keys = len(GEMINI_KEYS)
503
- if total_keys == 0:
504
- return[]
505
-
506
  actual_count = min(count, total_keys)
507
  selected_keys =[]
508
-
509
  for _ in range(actual_count):
510
  selected_keys.append(GEMINI_KEYS[current_gemini_key_index])
511
  current_gemini_key_index = (current_gemini_key_index + 1) % total_keys
512
-
513
  return selected_keys
514
 
515
  HF_TOKENS_STR = os.environ.get("HF_TOKENS", "")
516
  HF_TOKENS =[k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()]
517
 
518
-
519
  # ==================================================================
520
- # 🔥 تابع آپلود فایل فوق قدرتمند و ضدضربه (75 بار مقاومت در برابر 502)
521
  # ==================================================================
522
  async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""):
523
  abs_path = os.path.abspath(file_name)
524
  error_logs =[]
525
 
526
- # تعیین خودکار و هوشمندانه نوع فرمت ارسالی به روبیکا
527
- api_file_type = "Image" if file_type in["photo", "Image", "image"] else "Voice" if file_type in ["voice", "Voice", "audio"] else file_type
528
-
529
- # روبیکا با ارسال فایل‌های ogg غیر ویس و فایل‌های mp3 به عنوان File مشکل دارد و 502 میدهد.
530
- if api_file_type == "Voice" and not abs_path.lower().endswith('.ogg'):
531
- api_file_type = "Music"
532
- if api_file_type == "File" and abs_path.lower().endswith('.mp3'):
533
- api_file_type = "Music"
534
 
535
- # روش اول: استفاده مستقیم از API روبیکا با 15 بار پافشاری قدرتمند
536
  for attempt in range(15):
537
  try:
538
  url_request = f"https://botapi.rubika.ir/v3/{bot_token}/requestSendFile"
539
  url_send = f"https://botapi.rubika.ir/v3/{bot_token}/sendFile"
540
 
541
  async with aiohttp.ClientSession() as session:
542
- # 1. درخواست لینک آپلود
543
  async with session.post(url_request, json={"type": api_file_type}, timeout=30) as resp:
544
  if resp.status != 200:
545
  error_logs.append(f"Request HTTP {resp.status}")
@@ -547,39 +546,29 @@ async def helper_upload_file(client, chat_id, file_name, file_type="Image", capt
547
  continue
548
 
549
  req_data = await resp.json()
550
-
551
  if req_data.get("status") == "OK":
552
  upload_url = req_data.get("data", {}).get("upload_url")
553
-
554
  if upload_url:
555
  with open(abs_path, "rb") as f:
556
  form = aiohttp.FormData()
557
  form.add_field('file', f, filename=os.path.basename(abs_path))
558
-
559
- # 2. آپلود فایل در سرور (با تایم‌اوت 300 ثانیه مخصوص فایل‌های حجیم مثل پادکست)
560
  async with session.post(upload_url, data=form, timeout=300) as up_resp:
561
  if up_resp.status != 200:
562
  error_logs.append(f"Upload HTTP {up_resp.status}")
563
- if up_resp.status in[502, 503, 500]:
564
- await asyncio.sleep(3)
565
- else:
566
- await asyncio.sleep(1.5)
567
  continue
568
 
569
- try:
570
- up_data = await up_resp.json()
571
  except Exception as json_err:
572
  error_logs.append(f"JSON Decode Error: {json_err}")
573
  await asyncio.sleep(2)
574
  continue
575
 
576
- # 3. دریافت آیدی و ارسال نهایی پیام
577
  final_file_id = None
578
  if up_data.get("status") == "OK" or up_data.get("status_det") == "OK":
579
  if "data" in up_data and isinstance(up_data["data"], dict):
580
  final_file_id = up_data["data"].get("file_id") or up_data["data"].get("id")
581
- elif "file_id" in up_data:
582
- final_file_id = up_data.get("file_id")
583
 
584
  if final_file_id:
585
  send_payload = {
@@ -596,48 +585,53 @@ async def helper_upload_file(client, chat_id, file_name, file_type="Image", capt
596
  continue
597
 
598
  s_data = await send_resp.json()
599
- if s_data.get("status") == "OK":
600
- return True
601
- else:
602
- error_logs.append(f"SendFile API Error: {s_data}")
603
  else:
604
  if up_data.get("status") == "INVALID_INPUT" and api_file_type == "Voice":
605
  api_file_type = "Music"
606
  error_logs.append(f"Upload API Error: {up_data}")
607
- else:
608
- error_logs.append(f"Missing upload_url in Response: {req_data}")
609
- else:
610
- error_logs.append(f"Request API Error: {req_data}")
611
  except Exception as e:
612
  error_logs.append(f"Raw HTTP Error: {str(e)[:100]}")
613
  await asyncio.sleep(2.5)
614
 
615
- # روش دوم: بک‌آپ قدرتمند با کتابخانه rubpy با 10 بار پافشاری
616
  for attempt in range(10):
617
  try:
618
- if hasattr(client, "send_file"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
619
  await client.send_file(chat_id, abs_path)
620
  if caption:
621
  await send_with_keyboard(client, chat_id, caption, True)
622
  return True
623
- elif hasattr(client, "send_document"):
624
- await client.send_document(chat_id, abs_path, caption=caption)
625
- return True
626
  except Exception as e:
627
  err_msg = str(e)[:100]
628
  error_logs.append(f"Rubpy Send Error: {err_msg}")
629
- if "502" in err_msg or "timeout" in err_msg.lower() or "time" in err_msg.lower():
630
- await asyncio.sleep(4)
631
- continue
632
- else:
633
- await asyncio.sleep(2)
634
 
635
  return "\n".join(error_logs[-6:])
636
 
637
-
638
- WORKER_URLS =[
639
- "https://opera8-ttspro.hf.space/generate",
640
- ]
641
 
642
  SPEAKERS = {
643
  "1": ("شهاب (مرد)", "Charon"), "2": ("آوا (زن)", "Zephyr"), "3": ("نوید (مرد)", "Achird"),
@@ -653,10 +647,166 @@ SPEAKERS = {
653
  }
654
 
655
  user_states = {}
656
- processed_messages_list = user_credits_db.get("system_processed_messages",[])
657
- processed_message_ids = set(processed_messages_list)
658
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
659
 
 
 
 
660
  async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=None):
661
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
662
  creds = get_user_credits(str_chat_id)
@@ -701,7 +851,6 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
701
  history = history[-40:]
702
  if history[0]["role"] == "model": history = history[1:]
703
 
704
- # --- سیستم پافشاری (تلاش مجدد) برای دریافت جواب از هوش مصنوعی ---
705
  final_answer = None
706
  for attempt in range(3):
707
  keys_to_try = get_next_gemini_keys(100)
@@ -811,7 +960,6 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
811
  await asyncio.sleep(2.5)
812
  except Exception: await asyncio.sleep(2.5)
813
 
814
- # --- کسر اعتبار فقط در صورتی که حداقل یک بخش از پیام موفقیت‌آمیز ارسال شده باشد ---
815
  if success_sent and not creds.get("is_premium"):
816
  user_credits_db[str_chat_id]["chat"] -= 1
817
  save_db(user_credits_db)
@@ -819,7 +967,6 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
819
  except Exception:
820
  await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False)
821
 
822
-
823
  async def process_image(client, chat_id, prompt, size_choice="1"):
824
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
825
  creds = get_user_credits(str_chat_id)
@@ -889,7 +1036,6 @@ async def process_image(client, chat_id, prompt, size_choice="1"):
889
  short_preview = enhanced_prompt[:150] + "..." if len(enhanced_prompt) > 150 else enhanced_prompt
890
  proc_msg = await send_with_keyboard(client, chat_id, f"🎨 در حال طراحی عکس...\n\n📏 **ابعاد:** {size_name}\n📝 **پرامپت ساخته شده:**\n`{short_preview}`\n\n(ممکن است چند ثانیه زمان ببرد)", False)
891
 
892
- # --- سیستم پافشاری برای ساخت عکس ---
893
  generated_image = None
894
  last_error_log = "هیچ اتصالی برقرار نشد."
895
 
@@ -928,7 +1074,6 @@ async def process_image(client, chat_id, prompt, size_choice="1"):
928
  await asyncio.sleep(1)
929
  caption_text = f"🎨 تصویر شما با پرامپت هوشمند آماده شد!\n\n📏 ابعاد تصویر: {size_name}\n✨ ایده اولیه: {prompt}"
930
 
931
- # آپلود قدرتمند با 3 بار تلاش مجدد بیرونی
932
  upload_result = False
933
  error_log_img = ""
934
  for up_att in range(3):
@@ -940,7 +1085,6 @@ async def process_image(client, chat_id, prompt, size_choice="1"):
940
  error_log_img = res
941
  await asyncio.sleep(4)
942
 
943
- # --- کسر اعتبار فقط در صورت موفقیت کامل آپلود به کاربر ---
944
  if upload_result is True:
945
  if not creds.get("is_premium"):
946
  user_credits_db[str_chat_id]["image"] -= 1
@@ -952,7 +1096,6 @@ async def process_image(client, chat_id, prompt, size_choice="1"):
952
  except Exception as e:
953
  await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره و ارسال عکس:\n{str(e)[:150]}", True)
954
 
955
-
956
  async def translate_text_aloha(prompt_text):
957
  session_hash = ''.join(random.choices(string.ascii_lowercase + string.digits, k=11))
958
  join_url = "https://hamed744-translate-tts-aloha.hf.space/gradio_api/queue/join"
@@ -986,7 +1129,6 @@ async def translate_text_aloha(prompt_text):
986
 
987
  return prompt_text
988
 
989
-
990
  async def process_image_edit(client, chat_id, image_bytes, prompt):
991
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
992
  creds = get_user_credits(str_chat_id)
@@ -1002,7 +1144,6 @@ async def process_image_edit(client, chat_id, image_bytes, prompt):
1002
  if not translated_prompt or translated_prompt.strip() == "":
1003
  translated_prompt = prompt
1004
 
1005
- # --- سیستم پافشاری برای ویرایش عکس ---
1006
  generated_image = None
1007
  last_error_log = "سرور پاسخ نداد."
1008
 
@@ -1051,7 +1192,6 @@ async def process_image_edit(client, chat_id, image_bytes, prompt):
1051
  error_log_edit = res
1052
  await asyncio.sleep(4)
1053
 
1054
- # --- کسر اعتبار فقط پس از تحویل موفقیت آمیز ---
1055
  if upload_result is True:
1056
  if not creds.get("is_premium"):
1057
  user_credits_db[str_chat_id]["edit_image"] -= 1
@@ -1063,7 +1203,6 @@ async def process_image_edit(client, chat_id, image_bytes, prompt):
1063
  except Exception as e:
1064
  await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس ویرایش شده:\n{str(e)[:150]}", True)
1065
 
1066
-
1067
  async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1068
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1069
  creds = get_user_credits(str_chat_id)
@@ -1075,7 +1214,6 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1075
  payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
1076
  headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
1077
 
1078
- # --- سیستم پافشاری برای ساخت صدا ---
1079
  audio_bytes = None
1080
  last_error = "پاسخی دریافت نشد"
1081
 
@@ -1111,11 +1249,9 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1111
  await asyncio.to_thread(sync_write_file, file_name_mp3, audio_bytes)
1112
  await asyncio.sleep(1)
1113
 
1114
- # ارسال با فرمت Music و مقاومت ۳ برابری
1115
  upload_result_file = False
1116
  error_log_tts = ""
1117
  for up_att in range(3):
1118
- # 🔴 ارسال متن به صدا بصورت موزیک 🔴
1119
  res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", "✅ صدای شما با موفقیت آماده شد (فایل MP3):")
1120
  if res is True:
1121
  upload_result_file = True
@@ -1124,7 +1260,6 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1124
  error_log_tts = res
1125
  await asyncio.sleep(4)
1126
 
1127
- # --- کسر اعتبار فقط پس از تحویل موفق فایل ---
1128
  if upload_result_file is True:
1129
  if not creds.get("is_premium"):
1130
  user_credits_db[str_chat_id]["tts"] -= 1
@@ -1137,7 +1272,6 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1137
  await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
1138
  except Exception: traceback.print_exc()
1139
 
1140
-
1141
  async def process_podcast(client, chat_id, prompt):
1142
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1143
  creds = get_user_credits(str_chat_id)
@@ -1193,7 +1327,6 @@ async def process_podcast(client, chat_id, prompt):
1193
  payload_gen = {"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}
1194
  chunk_audio_bytes = None
1195
 
1196
- # --- پافشاری قدرتمندتر برای دریافت هر بخش پادکست ---
1197
  for attempt in range(10):
1198
  try:
1199
  async with session.post(url_generate, json=payload_gen, timeout=120) as resp:
@@ -1208,7 +1341,7 @@ async def process_podcast(client, chat_id, prompt):
1208
  try:
1209
  combined_audio = await asyncio.to_thread(sync_combine_audio, combined_audio, chunk_audio_bytes)
1210
  except Exception as e:
1211
- return await send_with_keyboard(client, chat_id, f"❌ خطا در پردازش صدا (آیا pydub و ffmpeg نصب است؟):\n{str(e)}", True)
1212
 
1213
  file_name_mp3 = f"final_podcast_{uuid.uuid4().hex}.mp3"
1214
  await asyncio.to_thread(sync_export_audio, combined_audio, file_name_mp3)
@@ -1222,12 +1355,9 @@ async def process_podcast(client, chat_id, prompt):
1222
 
1223
  caption_file = f"🎧 فایل پادکست شما با فرمت MP3:\n\n💡 موضوع شما: {prompt}"
1224
 
1225
- # ⚠️ آپلود بسیار قدرتمند پادکست (ارسال با هویت Music برای جلوگیری از 502)
1226
- # همراه با 3 مرحله تکرار خارجی، که خود شامل 15 + 10 بار تکرار داخلی است!
1227
  upload_result_file = False
1228
  error_log_pod = ""
1229
  for up_att in range(3):
1230
- # 🔴 ارسال پادکست بصورت موزیک 🔴
1231
  res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", caption_file)
1232
  if res is True:
1233
  upload_result_file = True
@@ -1236,7 +1366,6 @@ async def process_podcast(client, chat_id, prompt):
1236
  error_log_pod = res
1237
  await asyncio.sleep(5)
1238
 
1239
- # --- کسر اعتبار فقط در صورتی که فایل پادکست با موفقیت در روبیکا آپلود شده باشد ---
1240
  if upload_result_file is True:
1241
  if not creds.get("is_premium"):
1242
  user_credits_db[str_chat_id]["podcast"] -= 1
@@ -1246,7 +1375,6 @@ async def process_podcast(client, chat_id, prompt):
1246
 
1247
  if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
1248
 
1249
-
1250
  async def process_stt(client, chat_id, audio_bytes, file_name):
1251
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1252
  creds = get_user_credits(str_chat_id)
@@ -1262,7 +1390,6 @@ async def process_stt(client, chat_id, audio_bytes, file_name):
1262
 
1263
  prompt = "لطفاً این فایل صوتی/تصویری را با دقت کامل گوش بده و صحبت‌های داخل آن را کلمه به کلمه به متن تبدیل کن. هیچ توضیح اضافه‌ای نده."
1264
 
1265
- # --- سیستم پافشاری برای تبدیل صدا به متن ---
1266
  transcribed_text = None
1267
  for attempt in range(5):
1268
  keys_to_try = get_next_gemini_keys(100)
@@ -1289,14 +1416,12 @@ async def process_stt(client, chat_id, audio_bytes, file_name):
1289
 
1290
  if transcribed_text:
1291
  sent = await send_with_keyboard(client, chat_id, f"📝 **متن استخراج شده:**\n\n{transcribed_text}", True)
1292
- # --- کسر اعتبار فقط اگر پیام با موفقیت در روبیکا ارسال شد ---
1293
  if sent and not creds.get("is_premium"):
1294
  user_credits_db[str_chat_id]["stt"] -= 1
1295
  save_db(user_credits_db)
1296
  else:
1297
  await send_with_keyboard(client, chat_id, "❌ سرور شلوغ است فعلا بعدا امتحان کنید.", True)
1298
 
1299
-
1300
  async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
1301
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1302
  creds = get_user_credits(str_chat_id)
@@ -1320,7 +1445,6 @@ async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
1320
 
1321
  is_image = mime_type.startswith('image/')
1322
 
1323
- # --- سیستم پافشاری برای تحلیل فایل ---
1324
  final_answer = None
1325
  for attempt in range(5):
1326
  keys_to_try = get_next_gemini_keys(100)
@@ -1386,7 +1510,6 @@ async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
1386
 
1387
  if final_answer:
1388
  sent = await send_with_keyboard(client, chat_id, f"💡 **نتیجه تحلیل:**\n\n{final_answer}", True)
1389
- # --- کسر اعتبار فقط پس از ارسال موفقیت‌آمیز نتیجه ---
1390
  if sent and not creds.get("is_premium"):
1391
  user_credits_db[str_chat_id]["file"] -= 1
1392
  save_db(user_credits_db)
@@ -1396,7 +1519,6 @@ async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
1396
  else:
1397
  await send_with_keyboard(client, chat_id, "❌ تمامی سرورها شلوغ هستند. لطفاً بعداً امتحان کنید.", True)
1398
 
1399
-
1400
  async def process_create_file(client, chat_id, topic):
1401
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1402
  creds = get_user_credits(str_chat_id)
@@ -1410,7 +1532,6 @@ async def process_create_file(client, chat_id, topic):
1410
 
1411
  ai_prompt = f"یک مقاله بسیار جامع، کاملا حرفه‌ای، بسیار طولانی و با جزئیات کامل درباره موضوع زیر به زبان فارسی بنویس. فقط متن مقاله را بده و هیچ توضیح اضافه‌ای نده:\n\nموضوع: {topic}"
1412
 
1413
- # --- سیستم پافشاری برای تولید متن مقاله ---
1414
  article_text = None
1415
  for attempt in range(5):
1416
  keys_to_try = get_next_gemini_keys(100)
@@ -1466,11 +1587,8 @@ async def process_create_file(client, chat_id, topic):
1466
  converter_url = "https://opera8-texttopdf.hf.space/"
1467
  uid = uuid.uuid4().hex
1468
 
1469
- # =======================================================================
1470
- # ---- 1. پردازش و ارسال فایل PDF (دونه دونه و با پافشاری شدید) ----
1471
- # =======================================================================
1472
  pdf_success = False
1473
- for attempt in range(30): # تا 30 بار تلاش (تقریبا ول کن نیست)
1474
  try:
1475
  pdf_bytes = None
1476
  async with aiohttp.ClientSession() as session:
@@ -1500,22 +1618,16 @@ async def process_create_file(client, chat_id, topic):
1500
 
1501
  if res_upload is True:
1502
  pdf_success = True
1503
- break # با موفقیت ساخته و ارسال شد -> خروج از حلقه
1504
  except Exception as e:
1505
  print(f"PDF error (attempt {attempt+1}):", e)
1506
 
1507
- await asyncio.sleep(4) # در صورت خطا 4 ثانیه مکث و تلاش مجدد
1508
 
1509
- # =======================================================================
1510
- # استراحت بین دو فایل برای هضم شدن فایل قبلی توسط سیستم روبیکا
1511
- # =======================================================================
1512
  await asyncio.sleep(3.5)
1513
 
1514
- # =======================================================================
1515
- # ---- 2. پردازش و ارسال فایل Word (دونه دونه و با پافشاری شدید) ----
1516
- # =======================================================================
1517
  docx_success = False
1518
- for attempt in range(30): # تا 30 بار تلاش
1519
  try:
1520
  docx_bytes = None
1521
  async with aiohttp.ClientSession() as session:
@@ -1545,11 +1657,11 @@ async def process_create_file(client, chat_id, topic):
1545
 
1546
  if res_upload is True:
1547
  docx_success = True
1548
- break # با موفقیت ساخته و ارسال شد -> خروج از حلقه
1549
  except Exception as e:
1550
  print(f"DOCX error (attempt {attempt+1}):", e)
1551
 
1552
- await asyncio.sleep(4) # در صورت خطا 4 ثانیه مکث و تلاش مجدد
1553
 
1554
  try:
1555
  if proc_msg:
@@ -1558,7 +1670,6 @@ async def process_create_file(client, chat_id, topic):
1558
  if msg_id: await client.delete_messages(chat_id,[msg_id])
1559
  except Exception: pass
1560
 
1561
- # --- کسر اعتبار فقط در صورتی که حداقل یکی از فایل‌ها با موفقیت کامل آپلود شده باشد ---
1562
  if pdf_success and docx_success:
1563
  if not creds.get("is_premium"):
1564
  user_credits_db[str_chat_id]["chat"] -= 1
@@ -1572,7 +1683,9 @@ async def process_create_file(client, chat_id, topic):
1572
  else:
1573
  await send_with_keyboard(client, chat_id, "❌ متأسفانه پس از تلاش‌های مکرر، سرور قادر به ساخت و ارسال هیچ‌یک از فایل‌ها نبود و اعتباری از شما کسر نشد.", True)
1574
 
1575
-
 
 
1576
  if not bot_token:
1577
  print("خطا: توکن ربات روبیکا وارد نشده است!")
1578
  else:
@@ -1580,7 +1693,7 @@ else:
1580
 
1581
  @bot.on_update(filters.private)
1582
  async def main_handler(client, update):
1583
- global BOT_GUID, processed_messages_list, processed_message_ids
1584
 
1585
  try:
1586
  if not BOT_GUID:
@@ -1607,36 +1720,33 @@ else:
1607
  if not msg_id and msg_obj:
1608
  msg_id = msg_obj.get("message_id") if isinstance(msg_obj, dict) else getattr(msg_obj, "message_id", None)
1609
 
1610
- unique_msg_key = f"{chat_id}_{msg_id}" if msg_id else None
1611
- if unique_msg_key:
1612
- if unique_msg_key in processed_message_ids: return
1613
-
1614
- processed_message_ids.add(unique_msg_key)
1615
- processed_messages_list.append(unique_msg_key)
1616
-
1617
- if len(processed_messages_list) > 5000:
1618
- processed_messages_list = processed_messages_list[-5000:]
1619
- processed_message_ids.clear()
1620
- processed_message_ids.update(processed_messages_list)
1621
 
1622
- user_credits_db["system_processed_messages"] = processed_messages_list
1623
- save_db(user_credits_db)
 
 
 
 
 
1624
 
1625
  user_text = getattr(update, "text", "") or getattr(msg_obj, "text", "")
1626
  user_text_str = str(user_text).strip() if user_text else ""
1627
  user_text_lower = user_text_str.lower()
1628
 
1629
- str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1630
-
1631
- # بررسی اسپم فقط برای همین کاربر
1632
  if is_user_spamming(str_chat_id):
1633
  return
1634
 
1635
  if str_chat_id not in user_states:
1636
  user_states[str_chat_id] = {"mode": None, "text": "", "history":[], "file_bytes": None, "file_name": None}
1637
 
1638
- creds = get_user_credits(str_chat_id)
1639
-
1640
  if user_text_lower.startswith(f"{ADMIN_CODE} pro=") or user_text_lower.startswith(f"{ADMIN_CODE}pro="):
1641
  parts = user_text_str.split("=", 1)
1642
  if len(parts) >= 2:
@@ -1654,6 +1764,9 @@ else:
1654
  "tts": 5,
1655
  "file": 1,
1656
  "stt": 5,
 
 
 
1657
  "has_joined": True,
1658
  "invited_count": 0,
1659
  "used_referral": False,
@@ -1671,6 +1784,8 @@ else:
1671
  user_credits_db[target_id]["tts"] = 999999
1672
  user_credits_db[target_id]["file"] = 999999
1673
  user_credits_db[target_id]["stt"] = 999999
 
 
1674
 
1675
  save_db(user_credits_db)
1676
  await send_with_keyboard(client, chat_id, f"✅ حساب کاربر `{target_id}` به مدت ۳۰ روز شارژ شد و به پرو ارتقا یافت.", False)
@@ -1720,6 +1835,8 @@ else:
1720
  status_text = "🥉 نسخه رایگان (آزمایشی)\n⏳ وضعیت: سهمیه روزانه محدود"
1721
 
1722
  chat_rem = "نامحدود ∞" if is_prem else t_creds.get('chat', 0)
 
 
1723
  podcast_rem = "نامحدود ∞" if is_prem else t_creds.get('podcast', 0)
1724
  tts_rem = "نامحدود ∞" if is_prem else t_creds.get('tts', 0)
1725
  file_rem = "نامحدود ∞" if is_prem else t_creds.get('file', 0)
@@ -1741,6 +1858,8 @@ else:
1741
  💬 چت: {chat_rem}
1742
  🎨 تولید عکس: {image_rem}
1743
  🪄 ویرایش عکس: {edit_image_rem}
 
 
1744
  🎙 پادکست: {podcast_rem}
1745
  🗣 متن به صدا: {tts_rem}
1746
  📁 تحلیل فایل: {file_rem}
@@ -1825,6 +1944,8 @@ else:
1825
  daily_note = "*نکته: سهمیه شما هر روز ساعت ۰۰:۰۰ بامداد به صورت خودکار مجدداً شارژ می‌گردد.*"
1826
 
1827
  chat_rem = "نامحدود ∞" if is_prem else creds['chat']
 
 
1828
  podcast_rem = "نامحدود ∞" if is_prem else creds['podcast']
1829
  tts_rem = "نامحدود ∞" if is_prem else creds['tts']
1830
  file_rem = "نامحدود ∞" if is_prem else creds['file']
@@ -1845,6 +1966,8 @@ else:
1845
  - 💬 چت هوشمند: {chat_rem}
1846
  - 🎨 تولید تصویر: {image_rem} عدد
1847
  - 🪄 ویرایش تصویر: {edit_image_rem} عدد
 
 
1848
  - 🎙 ساخت پادکست: {podcast_rem}
1849
  - 🗣 تبدیل متن به صدا: {tts_rem}
1850
  - 📁 تحلیل فایل و سند: {file_rem}
@@ -1854,9 +1977,6 @@ else:
1854
  await send_with_keyboard(client, chat_id, account_profile, True)
1855
  return
1856
 
1857
- # ===============================================
1858
- # 🎁 سیستم جدید دعوت دوستان (دو سر سود) 🎁
1859
- # ===============================================
1860
  if user_text_str in["/invite", "دعوت دوستان 🎁"]:
1861
  invites = creds.get("invited_count", 0)
1862
  remains = 10 - (invites % 10)
@@ -1884,6 +2004,7 @@ else:
1884
  ✨ با این ربات می‌تونی کارهای زیر رو به راحتی انجام بدی:
1885
  💬 چت با پیشرفته‌ترین هوش مصنوعی
1886
  🎨 ساخت و ویرایش حرفه‌ای عکس
 
1887
  🎙 ساخت پادکست اختصاصی
1888
  🗣 تبدیل متن به صدا (30 گوینده مختلف)
1889
  📝 تبدیل صدا و ویدیو به متن
@@ -1913,6 +2034,7 @@ else:
1913
 
1914
  🎁 **بسته طلایی یک‌ماهه شامل:**
1915
  🤖 چت با هوش مصنوعی بصورت نامحدود
 
1916
  🗣 تبدیل متن به صدا بصورت نامحدود با ۳۰ گوینده
1917
  🎙 ساخت پادکست بصورت نامحدود
1918
 
@@ -1978,6 +2100,23 @@ else:
1978
  await send_with_keyboard(client, chat_id, "📻 شما وارد بخش **ساخت پادکست** شدید.\n\nلطفاً موضوع پادکست خود را بفرستید.\nمثال: درباره تاریخچه پیدایش قهوه با ۳ گوینده یک پادکست جذاب بساز . همچنین این قسمت متصل به مدل زبانی است و درخواست هارو قبل از ساخت درک میکنه. میتوانید مقاله کامل یک سایت بفرستید با تبلیغات یا هرچی، هوش مصنوعی متن مقاله رو استخراج و پادکست براتون میسازه . در توضیحات امکان مشخص کردن تعداد گوینده به همراه اسم شون نیز از سمت شما امکان پذیر است.\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
1979
  return
1980
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1981
  if user_text_str in["/file", "تحلیل فایل 📁"]:
1982
  user_states[str_chat_id]["mode"] = "file_waiting_for_file"
1983
  user_states[str_chat_id]["file_bytes"] = None
@@ -2001,9 +2140,6 @@ else:
2001
  elif user_text_str: await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا از کیبورد پایین، بخش مورد نظرتان را انتخاب کنید:", True)
2002
  return
2003
 
2004
- # ===============================================
2005
- # بخش دریافت کد هدیه 8 رقمی از کاربر
2006
- # ===============================================
2007
  elif current_mode == "waiting_for_referral_code":
2008
  if user_text_str:
2009
  normalized_code = to_english_digits(user_text_str).strip()
@@ -2071,9 +2207,6 @@ else:
2071
  await send_with_keyboard(client, chat_id, "✅ کد هدیه با موفقیت ثبت شد!\n🎁 **10 سهمیه تبدیل رایگان متن به صدا** به حساب شما اضافه گردید.", True)
2072
  return
2073
 
2074
- # ===============================================
2075
- # آپدیت جدید: در چت ابتدا فایل را می‌گیرد و می‌پرسد چه کار کند
2076
- # ===============================================
2077
  elif current_mode == "chat":
2078
  if is_file:
2079
  await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
@@ -2100,9 +2233,6 @@ else:
2100
  await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False)
2101
  return
2102
 
2103
- # ===============================================
2104
- # دریافت سایز قبل از ساخت عکس
2105
- # ===============================================
2106
  elif current_mode == "image_waiting_for_text":
2107
  if user_text_str:
2108
  user_states[str_chat_id]["text"] = user_text_str
@@ -2226,6 +2356,100 @@ else:
2226
  await send_with_keyboard(client, chat_id, "⚠️ لطفاً موضوع مقاله خود را به صورت متنی بفرستید.", False)
2227
  return
2228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2229
  except Exception: traceback.print_exc()
2230
 
2231
  if __name__ == "__main__":
@@ -2235,8 +2459,7 @@ if __name__ == "__main__":
2235
  loop = asyncio.get_event_loop()
2236
  loop.set_default_executor(concurrent.futures.ThreadPoolExecutor(max_workers=32))
2237
 
2238
- # روشن کردن سیستم آپلود دیتابیس هوشمند
2239
  loop.create_task(background_db_uploader())
2240
 
2241
- print("ربات آلفا پرو با سیستم اشتراک نامحدود + سپر امنیتی + دانلود پیشرفته فایل + 32 Worker پس‌زمینه روشن شد...")
2242
  bot.run()
 
16
  from flask import Flask
17
  from rubpy.bot import BotClient, filters
18
 
 
19
  from huggingface_hub import AsyncInferenceClient, HfApi, hf_hub_download
20
  from PIL import Image
21
  from pydub import AudioSegment
22
 
23
+ # --- تنظیمات آدرس سرورهای تغییر صدا ---
24
+ VC_BASE_URL = "https://sada8888-sada.hf.space"
25
+ LEGACY_BASE_URL = "https://ezmarynoori-rvc.hf.space"
26
+
27
+ LEGACY_MODELS = {
28
+ "1": {"name": "شادمهر", "url": "https://huggingface.co/amirmatrix/shadmehr/resolve/main/added_IVF722_Flat_nprobe_1_shadmehr_v2.zip?download=true", "gender": "male"},
29
+ "2": {"name": "معین", "url": "https://huggingface.co/datasets/Hamed744/Ezmary/resolve/main/Moein.zip?download=true", "gender": "male"},
30
+ "3": {"name": "بیلی آیلیش", "url": "https://huggingface.co/datasets/Hamed744/Ezmary/resolve/main/Billie.zip?download=true", "gender": "female"},
31
+ "4": {"name": "محسن چاوشی", "url": "https://huggingface.co/datasets/Hamed744/Ezmary/resolve/main/chavoshi250.zip?download=true", "gender": "male"},
32
+ "5": {"name": "سیاوش قمیشی", "url": "https://huggingface.co/datasets/Hamed744/Ezmary/resolve/main/Ghomayshi250.zip?download=true", "gender": "male"},
33
+ "6": {"name": "یاس", "url": "https://huggingface.co/datasets/Hamed744/Ezmary/resolve/main/Yas300.zip?download=true", "gender": "male"},
34
+ "7": {"name": "عادل فردوسی‌پور", "url": "https://huggingface.co/datasets/Hamed744/Ezmary/resolve/main/Adel.zip?download=true", "gender": "male"},
35
+ "8": {"name": "باب اسفنجی", "url": "https://huggingface.co/datasets/Hamed744/Ezmary/resolve/main/Bab_Asfanj300.zip?download=true", "gender": "male"}
36
+ }
37
+
38
+ STANDARD_MODELS = {
39
+ "9": {"name": "علی سورنا", "ref": "https://huggingface.co/datasets/Hamed744/mp3/resolve/main/%D9%85%D8%AF%D9%84%20%D8%B5%D8%AF%D8%A7%DB%8C%20%D8%B3%D9%88%D8%B1%D9%86%D8%A7.mp3?download=true"},
40
+ "10": {"name": "رونالدو", "ref": "https://huggingface.co/datasets/Hamed744/mp3/resolve/main/%D8%B1%D9%88%D9%86%D8%A7%D9%84%D8%AF%D9%88.wav?download=true"},
41
+ "11": {"name": "مسی", "ref": "https://huggingface.co/datasets/Hamed744/mp3/resolve/main/%D8%B5%D8%AF%D8%A7%DB%8C-%D9%85%D8%B3%DB%8C.mp3?download=true"},
42
+ "12": {"name": "مریم", "ref": "https://huggingface.co/datasets/Hamed744/mp3/resolve/main/%D8%B5%D8%AF%D8%A7%DB%8C%20%D8%AE%D8%A7%D9%86%D9%85.wav?download=true"}
43
+ }
44
 
45
+ # --- کد مدیریت ---
46
+ ADMIN_CODE = "3011"
47
  BOT_GUID = None
48
 
49
  # =======================================================
50
+ # سیستم ضد اسپم کاربر-محور
51
  # =======================================================
52
  user_message_times = {}
53
 
54
  def is_user_spamming(chat_id):
55
  now = time.time()
56
  times = user_message_times.get(chat_id,[])
57
+ times = [t for t in times if now - t < 3.0]
 
58
  times.append(now)
59
  user_message_times[chat_id] = times
 
 
60
  if len(times) > 4:
61
  return True
62
  return False
63
 
64
+ # --- سیستم دیتابیس حساب کاربری מתصل به دیتاست هاگینگ فیس ---
65
  DB_FILE = "users_db.json"
66
  DATASET_REPO = "opera8/Karbaran-rayegan-tedad"
67
  HF_TOKEN_DB = os.environ.get("HF_TOKEN")
 
88
 
89
  def load_db():
90
  print("در حال تلاش برای خواندن دیتابیس کاربران از دیتاست هاگینگ فیس...")
91
+ if not HF_TOKEN_DB:
92
+ if os.path.exists(DB_FILE):
93
+ try:
94
+ with open(DB_FILE, "r", encoding="utf-8") as f:
95
+ return json.load(f)
96
+ except Exception:
97
+ pass
98
+ return {}
99
+
100
+ while True:
101
  try:
102
  file_path = hf_hub_download(
103
  repo_id=DATASET_REPO,
 
109
  print("✅ دیتابیس با موفقیت از دیتاست هاگینگ فیس لود شد.")
110
  return json.load(f)
111
  except Exception as e:
112
+ err_msg = str(e)
113
+ if "EntryNotFoundError" in err_msg or "404" in err_msg:
114
+ print("⚠️ فایل دیتابیس در هاگینگ فیس یافت نشد (ساخت دیتابیس جدید).")
115
+ return {}
116
+
117
+ print(f"⚠️ قطعی در ارتباط با هاگینگ فیس! تلاش مجدد در 5 ثانیه... ({err_msg[:50]})")
118
+ time.sleep(5)
 
 
119
 
120
  db_needs_upload = False
121
 
 
124
  with db_lock:
125
  try:
126
  with open(DB_FILE, "w", encoding="utf-8") as f:
127
+ json.dump(db_data, f, ensure_ascii=False, separators=(',', ':'))
128
+ db_needs_upload = True
129
  except Exception as e:
130
  print("خطا در ذخیره دیتابیس:", e)
131
 
 
132
  async def background_db_uploader():
133
  global db_needs_upload
134
  while True:
135
+ await asyncio.sleep(300)
136
  if db_needs_upload and HF_TOKEN_DB:
137
  db_needs_upload = False
138
  api = HfApi(token=HF_TOKEN_DB)
139
  try:
 
140
  await asyncio.to_thread(
141
  api.upload_file,
142
  path_or_fileobj=DB_FILE,
 
147
  print("✅ بکاپ دیتابیس در هاگینگ فیس ذخیره شد.")
148
  except Exception as e:
149
  print("❌ خطا در آپلود دیتابیس به هاگینگ فیس:", str(e)[:100])
150
+ db_needs_upload = True
151
 
152
  user_credits_db = load_db()
153
 
 
156
  if not user_data.get("referral_code"):
157
  while True:
158
  new_code = ''.join(random.choices(string.digits, k=8))
 
159
  exists = any(isinstance(u, dict) and u.get("referral_code") == new_code for u in user_credits_db.values())
160
  if not exists:
161
  user_data["referral_code"] = new_code
 
186
  "tts": 5,
187
  "file": 1,
188
  "stt": 5,
189
+ "voice_conv": 3,
190
+ "voice_clone": 1,
191
+ "last_msg_id": 0,
192
  "has_joined": False,
193
  "invited_count": 0,
194
  "used_referral": False,
 
197
  save_db(user_credits_db)
198
 
199
  user_data = user_credits_db[str_chat_id]
200
+
201
+ if "voice_conv" not in user_data: user_data["voice_conv"] = 3
202
+ if "voice_clone" not in user_data: user_data["voice_clone"] = 1
203
+ if "last_msg_id" not in user_data: user_data["last_msg_id"] = 0
204
+
205
  is_premium = user_data.get("is_premium", False)
206
 
207
  if is_premium and user_data.get("expire_date"):
 
224
  user_data["tts"] = 5
225
  user_data["file"] = 1
226
  user_data["stt"] = 5
227
+ user_data["voice_conv"] = 3
228
+ user_data["voice_clone"] = 1
229
  save_db(user_credits_db)
230
 
231
  return user_data
232
 
233
  def to_english_digits(text):
234
+ if not text: return text
 
235
  persian_digits = '۰۱۲۳۴۵۶۷۸۹'
236
  arabic_digits = '٠١٢٣٤٥٦٧٨٩'
237
  english_digits = '0123456789'
 
243
 
244
  @app.route('/')
245
  def home():
246
+ return "ربات یکپارچه آلفا (نسخه پرو + مدیریت اشتراک نامحدود + دعوت دوستان + سیستم تغییر صدا) روشن است! 🚀"
247
 
248
  def run_flask():
249
  app.run(host="0.0.0.0", port=7860, threaded=True)
250
 
251
+ # --- توابع کمکی ---
252
  def sync_save_image(image, file_name):
253
  rgb_im = image.convert('RGB')
254
  rgb_im.save(file_name, format="JPEG", quality=100)
 
288
  {"id": "tts_btn", "type": "Simple", "button_text": "تبدیل متن به صدا🗣️"}
289
  ]
290
  },
291
+ {
292
+ "buttons":[
293
+ {"id": "vc_btn", "type": "Simple", "button_text": "تغییر صدا 🎙️"},
294
+ {"id": "clone_btn", "type": "Simple", "button_text": "کلون کردن صدا 👤"}
295
+ ]
296
+ },
297
  {
298
  "buttons":[
299
  {"id": "stt_btn", "type": "Simple", "button_text": "فایل صوتی به متن 📝"},
 
367
  elif hasattr(res, 'exist') and hasattr(res.exist, 'chat'):
368
  CHANNEL_GUID = getattr(res.exist.chat, 'object_guid', None)
369
 
370
+ if not CHANNEL_GUID: return True
 
371
 
372
+ payload = {"channel_guid": CHANNEL_GUID, "member_guid": user_id}
 
 
 
373
  res = await client._make_request("getChannelParticipant", payload)
374
 
375
+ if isinstance(res, dict) and res.get("status") == "OK":
376
+ data = res.get("data", {})
377
+ if data and "participant" in data: return True
378
+ elif hasattr(res, 'status') and getattr(res, 'status') == "OK":
379
+ return True
 
 
 
 
 
 
380
  return False
381
+ except Exception: return False
382
 
383
  async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
384
  try:
385
+ if not use_keyboard: return await client.send_message(chat_id, text)
386
+ payload = {"chat_id": chat_id, "text": text, "chat_keypad_type": "New", "chat_keypad": MAIN_KEYPAD_DICT}
 
 
 
 
 
 
 
387
  return await client._make_request("sendMessage", payload)
388
  except Exception:
389
+ try: return await client.send_message(chat_id, text)
390
+ except Exception: return None
 
 
 
391
 
392
  bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
393
 
394
  # ==================================================================
395
+ # تابع دانلود ضد بمب اتم (پافشاری ۳۵ بار)
396
  # ==================================================================
397
  async def helper_download_file(client, msg_obj):
398
  errors =[]
399
  file_obj = None
400
  file_id = None
401
 
 
402
  for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
403
  val = getattr(msg_obj, attr, None)
404
  if val:
405
  file_obj = val
406
+ if hasattr(val, 'file_id'): file_id = val.file_id
407
+ elif isinstance(val, dict) and 'file_id' in val: file_id = val['file_id']
 
 
408
  break
409
 
410
  if not file_obj and hasattr(msg_obj, "file_id"):
411
  file_id = msg_obj.file_id
412
  file_obj = msg_obj
413
 
414
+ if not file_id: raise Exception("خطا: هیچ فایلی در پیام یافت نشد.")
 
415
 
416
  temp_name = f"temp_dl_{uuid.uuid4().hex}.tmp"
417
 
 
418
  for attempt in range(20):
419
  try:
420
  url_get_file = f"https://botapi.rubika.ir/v3/{bot_token}/getFile"
 
428
  if res_data.get("status") == "OK" or res_data.get("status_det") == "OK":
429
  download_url = res_data.get("data", {}).get("download_url") or res_data.get("data", {}).get("file_url")
430
  if download_url:
 
431
  for dl_attempt in range(3):
432
  try:
433
  async with session.get(download_url, headers=headers, timeout=60) as dl_resp:
434
  if dl_resp.status == 200:
435
  data = await dl_resp.read()
436
+ if data and len(data) > 0: return data
437
+ else: errors.append(f"DL HTTP {dl_resp.status}")
 
 
438
  await asyncio.sleep(2)
439
  except Exception as dl_err:
440
  errors.append(f"DL Error: {str(dl_err)[:30]}")
441
  await asyncio.sleep(2)
442
+ else: errors.append("No download URL in response.")
443
+ else: errors.append(f"API Not OK: {res_data.get('status')}")
444
+ elif resp.status in [502, 503, 500]:
 
 
445
  errors.append(f"GetFile HTTP {resp.status}")
446
+ await asyncio.sleep(3.5)
447
  continue
448
  else:
449
  errors.append(f"GetFile HTTP {resp.status}")
 
452
  errors.append(f"API Error: {str(e)[:50]}")
453
  await asyncio.sleep(2)
454
 
 
455
  if file_obj:
456
  for attempt in range(10):
457
  try:
458
  if hasattr(client, "download"):
459
  result = await client.download(file_obj, save_as=temp_name)
460
+ if isinstance(result, bytes) and len(result) > 0: return result
 
461
  if os.path.exists(temp_name):
462
  data = await asyncio.to_thread(sync_read_file, temp_name)
463
  os.remove(temp_name)
464
+ if data and len(data) > 0: return data
 
465
  except Exception as e:
466
+ errors.append(f"Rubpy Obj Error: {str(e)[:50]}")
 
467
  await asyncio.sleep(3)
468
 
 
469
  for attempt in range(5):
470
  try:
471
  if hasattr(client, "download_file"):
 
473
  if os.path.exists(temp_name):
474
  data = await asyncio.to_thread(sync_read_file, temp_name)
475
  os.remove(temp_name)
476
+ if data and len(data) > 0: return data
 
477
  except Exception as e:
478
  errors.append(f"Rubpy FileId Error: {str(e)[:50]}")
479
  await asyncio.sleep(3)
480
 
481
  raise Exception(f"سرورهای دانلود روبیکا پس از ۳۵ بار تلاش پاسخ ندادند!\nلاگ خطاها: {str(errors[-5:])}")
482
 
483
+ async def helper_download_url_to_bytes(url):
484
+ async with aiohttp.ClientSession() as session:
485
+ for _ in range(3):
486
+ try:
487
+ async with session.get(url, timeout=30) as resp:
488
+ if resp.status == 200:
489
+ return await resp.read()
490
+ except Exception:
491
+ await asyncio.sleep(2)
492
+ return None
493
 
494
+ # ==================================================================
495
+ # توکن‌ها و کلیدها
496
+ # ==================================================================
497
  GEMINI_KEYS_STR1 = os.environ.get("GEMINI_API_KEYS1", "")
498
  GEMINI_KEYS_STR2 = os.environ.get("GEMINI_API_KEYS2", "")
499
 
500
  _raw_keys =[]
501
+ if GEMINI_KEYS_STR1: _raw_keys.extend(GEMINI_KEYS_STR1.split(","))
502
+ if GEMINI_KEYS_STR2: _raw_keys.extend(GEMINI_KEYS_STR2.split(","))
 
 
503
 
504
  GEMINI_KEYS = list(set([k.strip() for k in _raw_keys if k.strip()]))
505
  print(f"✅ تعداد {len(GEMINI_KEYS)} کلید جیمینای با موفقیت شناسایی شد.")
 
511
  global current_gemini_key_index
512
  with gemini_key_lock:
513
  total_keys = len(GEMINI_KEYS)
514
+ if total_keys == 0: return[]
 
 
515
  actual_count = min(count, total_keys)
516
  selected_keys =[]
 
517
  for _ in range(actual_count):
518
  selected_keys.append(GEMINI_KEYS[current_gemini_key_index])
519
  current_gemini_key_index = (current_gemini_key_index + 1) % total_keys
 
520
  return selected_keys
521
 
522
  HF_TOKENS_STR = os.environ.get("HF_TOKENS", "")
523
  HF_TOKENS =[k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()]
524
 
 
525
  # ==================================================================
526
+ # تابع آپلود فایل (قدرتمند و ضدضربه) با حفظ قابلیت ارسال عکس
527
  # ==================================================================
528
  async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""):
529
  abs_path = os.path.abspath(file_name)
530
  error_logs =[]
531
 
532
+ api_file_type = "Image" if file_type in ["photo", "Image", "image"] else "Voice" if file_type in["voice", "Voice", "audio"] else file_type
533
+ if api_file_type == "Voice" and not abs_path.lower().endswith('.ogg'): api_file_type = "Music"
534
+ if api_file_type == "File" and abs_path.lower().endswith('.mp3'): api_file_type = "Music"
 
 
 
 
 
535
 
 
536
  for attempt in range(15):
537
  try:
538
  url_request = f"https://botapi.rubika.ir/v3/{bot_token}/requestSendFile"
539
  url_send = f"https://botapi.rubika.ir/v3/{bot_token}/sendFile"
540
 
541
  async with aiohttp.ClientSession() as session:
 
542
  async with session.post(url_request, json={"type": api_file_type}, timeout=30) as resp:
543
  if resp.status != 200:
544
  error_logs.append(f"Request HTTP {resp.status}")
 
546
  continue
547
 
548
  req_data = await resp.json()
 
549
  if req_data.get("status") == "OK":
550
  upload_url = req_data.get("data", {}).get("upload_url")
 
551
  if upload_url:
552
  with open(abs_path, "rb") as f:
553
  form = aiohttp.FormData()
554
  form.add_field('file', f, filename=os.path.basename(abs_path))
 
 
555
  async with session.post(upload_url, data=form, timeout=300) as up_resp:
556
  if up_resp.status != 200:
557
  error_logs.append(f"Upload HTTP {up_resp.status}")
558
+ await asyncio.sleep(3) if up_resp.status in[502, 503, 500] else asyncio.sleep(1.5)
 
 
 
559
  continue
560
 
561
+ try: up_data = await up_resp.json()
 
562
  except Exception as json_err:
563
  error_logs.append(f"JSON Decode Error: {json_err}")
564
  await asyncio.sleep(2)
565
  continue
566
 
 
567
  final_file_id = None
568
  if up_data.get("status") == "OK" or up_data.get("status_det") == "OK":
569
  if "data" in up_data and isinstance(up_data["data"], dict):
570
  final_file_id = up_data["data"].get("file_id") or up_data["data"].get("id")
571
+ elif "file_id" in up_data: final_file_id = up_data.get("file_id")
 
572
 
573
  if final_file_id:
574
  send_payload = {
 
585
  continue
586
 
587
  s_data = await send_resp.json()
588
+ if s_data.get("status") == "OK": return True
589
+ else: error_logs.append(f"SendFile API Error: {s_data}")
 
 
590
  else:
591
  if up_data.get("status") == "INVALID_INPUT" and api_file_type == "Voice":
592
  api_file_type = "Music"
593
  error_logs.append(f"Upload API Error: {up_data}")
594
+ else: error_logs.append(f"Missing upload_url in Response: {req_data}")
595
+ else: error_logs.append(f"Request API Error: {req_data}")
 
 
596
  except Exception as e:
597
  error_logs.append(f"Raw HTTP Error: {str(e)[:100]}")
598
  await asyncio.sleep(2.5)
599
 
600
+ # روش دوم: استفاده از متدهای اختصاصی rubpy برای ارسال عکس/فایل (جلوگیری از ارسال عکس به صورت داکیومنت)
601
  for attempt in range(10):
602
  try:
603
+ if api_file_type == "Image" and hasattr(client, "send_photo"):
604
+ await client.send_photo(chat_id, photo=abs_path, caption=caption)
605
+ return True
606
+ elif api_file_type in ["Voice", "Music"]:
607
+ if hasattr(client, "send_voice"):
608
+ await client.send_voice(chat_id, voice=abs_path, caption=caption)
609
+ return True
610
+ elif hasattr(client, "send_music"):
611
+ await client.send_music(chat_id, music=abs_path, caption=caption)
612
+ return True
613
+
614
+ # در صورتی که هیچ‌کدام از شروط بالا برقرار نبود به عنوان فایل ارسال شود
615
+ if hasattr(client, "send_document"):
616
+ await client.send_document(chat_id, document=abs_path, caption=caption)
617
+ return True
618
+ elif hasattr(client, "send_file"):
619
  await client.send_file(chat_id, abs_path)
620
  if caption:
621
  await send_with_keyboard(client, chat_id, caption, True)
622
  return True
 
 
 
623
  except Exception as e:
624
  err_msg = str(e)[:100]
625
  error_logs.append(f"Rubpy Send Error: {err_msg}")
626
+ if "502" in err_msg or "timeout" in err_msg.lower() or "time" in err_msg.lower(): await asyncio.sleep(4)
627
+ else: await asyncio.sleep(2)
 
 
 
628
 
629
  return "\n".join(error_logs[-6:])
630
 
631
+ # ==================================================================
632
+ # لیست‌های اولیه ربات
633
+ # ==================================================================
634
+ WORKER_URLS =["https://opera8-ttspro.hf.space/generate"]
635
 
636
  SPEAKERS = {
637
  "1": ("شهاب (مرد)", "Charon"), "2": ("آوا (زن)", "Zephyr"), "3": ("نوید (مرد)", "Achird"),
 
647
  }
648
 
649
  user_states = {}
 
 
650
 
651
+ # ==================================================================
652
+ # توابع تغییر صدا و کلون کردن
653
+ # ==================================================================
654
+ async def process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, job_type_name, credit_type):
655
+ str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
656
+ creds = get_user_credits(str_chat_id)
657
+
658
+ proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال آماده‌سازی فایل‌ها...\n(مدل: {job_type_name})", False)
659
+
660
+ async with aiohttp.ClientSession() as session:
661
+ form = aiohttp.FormData()
662
+ form.add_field('source_audio', src_bytes, filename='src.wav', content_type='audio/wav')
663
+ form.add_field('ref_audio', ref_bytes, filename='ref.wav', content_type='audio/wav')
664
+
665
+ try:
666
+ async with session.post(f"{VC_BASE_URL}/upload", data=form, timeout=43200) as resp:
667
+ if resp.status != 200:
668
+ return await send_with_keyboard(client, chat_id, f"❌ خطا در ارسال فایل‌ها (کد {resp.status})", True)
669
+ data = await resp.json()
670
+ job_id = data.get("job_id")
671
+ total_chunks = data.get("total_chunks", 1)
672
+ chunks = data.get("chunks",[])
673
+ except Exception as e:
674
+ return await send_with_keyboard(client, chat_id, f"❌ خطا در اتصال به سرور:\n{str(e)[:100]}", True)
675
+
676
+ await send_with_keyboard(client, chat_id, "✅ فایل‌ها ارسال شد. در حال پردازش و تغییر صدا...\n(لطفا چند دقیقه صبور باشید)", False)
677
+
678
+ final_filename = None
679
+ for _ in range(10000):
680
+ await asyncio.sleep(4)
681
+ payload_check = {"job_id": job_id, "total_chunks": total_chunks, "chunks": chunks}
682
+ try:
683
+ async with session.post(f"{VC_BASE_URL}/check_status", json=payload_check, timeout=20) as c_resp:
684
+ if c_resp.status == 200:
685
+ c_data = await c_resp.json()
686
+ if c_data.get("status") == "completed":
687
+ final_filename = c_data.get("filename")
688
+ break
689
+ elif c_data.get("status") in ["failed", "error"]:
690
+ return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش صدا.", True)
691
+ except Exception:
692
+ pass
693
+
694
+ if not final_filename:
695
+ return await send_with_keyboard(client, chat_id, "❌ پردازش بیش از حد طول کشید و متوقف شد.", True)
696
+
697
+ download_url = f"{VC_BASE_URL}/download/{final_filename}"
698
+ await send_with_keyboard(client, chat_id, "📥 پردازش تمام شد! در حال دانلود نتیجه...", False)
699
+ try:
700
+ async with session.get(download_url, timeout=43200) as d_resp:
701
+ if d_resp.status == 200:
702
+ result_bytes = await d_resp.read()
703
+ else:
704
+ return await send_with_keyboard(client, chat_id, "❌ خطا در دریافت فایل نهایی از سرور.", True)
705
+ except Exception:
706
+ return await send_with_keyboard(client, chat_id, "❌ خطا در اتصال هنگام دانلود خروجی.", True)
707
+
708
+ file_name_mp3 = f"vc_standard_{uuid.uuid4().hex[:6]}.mp3"
709
+ await asyncio.to_thread(sync_write_file, file_name_mp3, result_bytes)
710
+
711
+ upload_result = False
712
+ for up_att in range(3):
713
+ res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", f"🎙️ {job_type_name} شما آماده است!")
714
+ if res is True:
715
+ upload_result = True
716
+ break
717
+ await asyncio.sleep(4)
718
+
719
+ if upload_result is True:
720
+ if not creds.get("is_premium"):
721
+ user_credits_db[str_chat_id][credit_type] -= 1
722
+ save_db(user_credits_db)
723
+ else:
724
+ await send_with_keyboard(client, chat_id, "❌ فایل پردازش شد اما امکان ارسال در روبیکا فراهم نشد.", True)
725
+
726
+ if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
727
+
728
+ async def process_legacy_vc_job(client, chat_id, src_bytes, model_url, pitch, model_name):
729
+ str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
730
+ creds = get_user_credits(str_chat_id)
731
+
732
+ proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال آماده‌سازی فایل‌ها...\n(مدل: {model_name})", False)
733
+
734
+ async with aiohttp.ClientSession() as session:
735
+ form = aiohttp.FormData()
736
+ form.add_field('audio_file', src_bytes, filename='input.wav', content_type='audio/wav')
737
+ form.add_field('model_url', model_url)
738
+ form.add_field('pitch', str(pitch))
739
+ form.add_field('algo', 'rmvpe+')
740
+ form.add_field('index_inf', '0.75')
741
+ form.add_field('res_filter', '3')
742
+ form.add_field('env_ratio', '0.25')
743
+ form.add_field('protect', '0.33')
744
+ form.add_field('denoise', 'false')
745
+ form.add_field('reverb', 'false')
746
+
747
+ try:
748
+ async with session.post(f"{LEGACY_BASE_URL}/upload", data=form, timeout=43200) as resp:
749
+ if resp.status != 200:
750
+ return await send_with_keyboard(client, chat_id, f"❌ خطا در ارسال فایل‌ها (کد {resp.status})", True)
751
+ data = await resp.json()
752
+ job_id = data.get("job_id")
753
+ except Exception as e:
754
+ return await send_with_keyboard(client, chat_id, f"❌ خطا در اتصال به سرور:\n{str(e)[:100]}", True)
755
+
756
+ await send_with_keyboard(client, chat_id, "✅ فایل ارسال شد. در حال پردازش و تغییر صدای شما...\n(لطفا چند دقیقه صبور باشید)", False)
757
+
758
+ final_filename = None
759
+ for _ in range(10000):
760
+ await asyncio.sleep(5)
761
+ try:
762
+ async with session.get(f"{LEGACY_BASE_URL}/status/{job_id}", timeout=20) as c_resp:
763
+ if c_resp.status == 200:
764
+ c_data = await c_resp.json()
765
+ if c_data.get("status") == "completed":
766
+ final_filename = c_data.get("filename")
767
+ break
768
+ elif c_data.get("status") in["failed", "error", "not_found"]:
769
+ return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش.", True)
770
+ except Exception:
771
+ pass
772
+
773
+ if not final_filename:
774
+ return await send_with_keyboard(client, chat_id, "❌ پردازش بیش از حد طول کشید و متوقف شد.", True)
775
+
776
+ download_url = f"{LEGACY_BASE_URL}/download/{final_filename}"
777
+ await send_with_keyboard(client, chat_id, "📥 پردازش تمام شد! در حال دانلود نتیجه...", False)
778
+ try:
779
+ async with session.get(download_url, timeout=43200) as d_resp:
780
+ if d_resp.status == 200:
781
+ result_bytes = await d_resp.read()
782
+ else:
783
+ return await send_with_keyboard(client, chat_id, "❌ خطا در دریافت فایل نهایی از سرور.", True)
784
+ except Exception:
785
+ return await send_with_keyboard(client, chat_id, "❌ خطا در اتصال هنگام دانلود خروجی.", True)
786
+
787
+ file_name_mp3 = f"vc_legacy_{uuid.uuid4().hex[:6]}.mp3"
788
+ await asyncio.to_thread(sync_write_file, file_name_mp3, result_bytes)
789
+
790
+ upload_result = False
791
+ for up_att in range(3):
792
+ res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", f"🎙️ صدای خروجی با مدل {model_name} آماده است!")
793
+ if res is True:
794
+ upload_result = True
795
+ break
796
+ await asyncio.sleep(4)
797
+
798
+ if upload_result is True:
799
+ if not creds.get("is_premium"):
800
+ user_credits_db[str_chat_id]["voice_conv"] -= 1
801
+ save_db(user_credits_db)
802
+ else:
803
+ await send_with_keyboard(client, chat_id, "❌ فایل پردازش شد اما امکان ارسال در روبیکا فراهم نشد.", True)
804
+
805
+ if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
806
 
807
+ # ==================================================================
808
+ # سایر توابع پردازشی
809
+ # ==================================================================
810
  async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=None):
811
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
812
  creds = get_user_credits(str_chat_id)
 
851
  history = history[-40:]
852
  if history[0]["role"] == "model": history = history[1:]
853
 
 
854
  final_answer = None
855
  for attempt in range(3):
856
  keys_to_try = get_next_gemini_keys(100)
 
960
  await asyncio.sleep(2.5)
961
  except Exception: await asyncio.sleep(2.5)
962
 
 
963
  if success_sent and not creds.get("is_premium"):
964
  user_credits_db[str_chat_id]["chat"] -= 1
965
  save_db(user_credits_db)
 
967
  except Exception:
968
  await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False)
969
 
 
970
  async def process_image(client, chat_id, prompt, size_choice="1"):
971
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
972
  creds = get_user_credits(str_chat_id)
 
1036
  short_preview = enhanced_prompt[:150] + "..." if len(enhanced_prompt) > 150 else enhanced_prompt
1037
  proc_msg = await send_with_keyboard(client, chat_id, f"🎨 در حال طراحی عکس...\n\n📏 **ابعاد:** {size_name}\n📝 **پرامپت ساخته شده:**\n`{short_preview}`\n\n(ممکن است چند ثانیه زمان ببرد)", False)
1038
 
 
1039
  generated_image = None
1040
  last_error_log = "هیچ اتصالی برقرار نشد."
1041
 
 
1074
  await asyncio.sleep(1)
1075
  caption_text = f"🎨 تصویر شما با پرامپت هوشمند آماده شد!\n\n📏 ابعاد تصویر: {size_name}\n✨ ایده اولیه: {prompt}"
1076
 
 
1077
  upload_result = False
1078
  error_log_img = ""
1079
  for up_att in range(3):
 
1085
  error_log_img = res
1086
  await asyncio.sleep(4)
1087
 
 
1088
  if upload_result is True:
1089
  if not creds.get("is_premium"):
1090
  user_credits_db[str_chat_id]["image"] -= 1
 
1096
  except Exception as e:
1097
  await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره و ارسال عکس:\n{str(e)[:150]}", True)
1098
 
 
1099
  async def translate_text_aloha(prompt_text):
1100
  session_hash = ''.join(random.choices(string.ascii_lowercase + string.digits, k=11))
1101
  join_url = "https://hamed744-translate-tts-aloha.hf.space/gradio_api/queue/join"
 
1129
 
1130
  return prompt_text
1131
 
 
1132
  async def process_image_edit(client, chat_id, image_bytes, prompt):
1133
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1134
  creds = get_user_credits(str_chat_id)
 
1144
  if not translated_prompt or translated_prompt.strip() == "":
1145
  translated_prompt = prompt
1146
 
 
1147
  generated_image = None
1148
  last_error_log = "سرور پاسخ نداد."
1149
 
 
1192
  error_log_edit = res
1193
  await asyncio.sleep(4)
1194
 
 
1195
  if upload_result is True:
1196
  if not creds.get("is_premium"):
1197
  user_credits_db[str_chat_id]["edit_image"] -= 1
 
1203
  except Exception as e:
1204
  await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس ویرایش شده:\n{str(e)[:150]}", True)
1205
 
 
1206
  async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1207
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1208
  creds = get_user_credits(str_chat_id)
 
1214
  payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
1215
  headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
1216
 
 
1217
  audio_bytes = None
1218
  last_error = "پاسخی دریافت نشد"
1219
 
 
1249
  await asyncio.to_thread(sync_write_file, file_name_mp3, audio_bytes)
1250
  await asyncio.sleep(1)
1251
 
 
1252
  upload_result_file = False
1253
  error_log_tts = ""
1254
  for up_att in range(3):
 
1255
  res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", "✅ صدای شما با موفقیت آماده شد (فایل MP3):")
1256
  if res is True:
1257
  upload_result_file = True
 
1260
  error_log_tts = res
1261
  await asyncio.sleep(4)
1262
 
 
1263
  if upload_result_file is True:
1264
  if not creds.get("is_premium"):
1265
  user_credits_db[str_chat_id]["tts"] -= 1
 
1272
  await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
1273
  except Exception: traceback.print_exc()
1274
 
 
1275
  async def process_podcast(client, chat_id, prompt):
1276
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1277
  creds = get_user_credits(str_chat_id)
 
1327
  payload_gen = {"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}
1328
  chunk_audio_bytes = None
1329
 
 
1330
  for attempt in range(10):
1331
  try:
1332
  async with session.post(url_generate, json=payload_gen, timeout=120) as resp:
 
1341
  try:
1342
  combined_audio = await asyncio.to_thread(sync_combine_audio, combined_audio, chunk_audio_bytes)
1343
  except Exception as e:
1344
+ return await send_with_keyboard(client, chat_id, f"❌ خطا در پردازش صدا:\n{str(e)}", True)
1345
 
1346
  file_name_mp3 = f"final_podcast_{uuid.uuid4().hex}.mp3"
1347
  await asyncio.to_thread(sync_export_audio, combined_audio, file_name_mp3)
 
1355
 
1356
  caption_file = f"🎧 فایل پادکست شما با فرمت MP3:\n\n💡 موضوع شما: {prompt}"
1357
 
 
 
1358
  upload_result_file = False
1359
  error_log_pod = ""
1360
  for up_att in range(3):
 
1361
  res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", caption_file)
1362
  if res is True:
1363
  upload_result_file = True
 
1366
  error_log_pod = res
1367
  await asyncio.sleep(5)
1368
 
 
1369
  if upload_result_file is True:
1370
  if not creds.get("is_premium"):
1371
  user_credits_db[str_chat_id]["podcast"] -= 1
 
1375
 
1376
  if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
1377
 
 
1378
  async def process_stt(client, chat_id, audio_bytes, file_name):
1379
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1380
  creds = get_user_credits(str_chat_id)
 
1390
 
1391
  prompt = "لطفاً این فایل صوتی/تصویری را با دقت کامل گوش بده و صحبت‌های داخل آن را کلمه به کلمه به متن تبدیل کن. هیچ توضیح اضافه‌ای نده."
1392
 
 
1393
  transcribed_text = None
1394
  for attempt in range(5):
1395
  keys_to_try = get_next_gemini_keys(100)
 
1416
 
1417
  if transcribed_text:
1418
  sent = await send_with_keyboard(client, chat_id, f"📝 **متن استخراج شده:**\n\n{transcribed_text}", True)
 
1419
  if sent and not creds.get("is_premium"):
1420
  user_credits_db[str_chat_id]["stt"] -= 1
1421
  save_db(user_credits_db)
1422
  else:
1423
  await send_with_keyboard(client, chat_id, "❌ سرور شلوغ است فعلا بعدا امتحان کنید.", True)
1424
 
 
1425
  async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
1426
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1427
  creds = get_user_credits(str_chat_id)
 
1445
 
1446
  is_image = mime_type.startswith('image/')
1447
 
 
1448
  final_answer = None
1449
  for attempt in range(5):
1450
  keys_to_try = get_next_gemini_keys(100)
 
1510
 
1511
  if final_answer:
1512
  sent = await send_with_keyboard(client, chat_id, f"💡 **نتیجه تحلیل:**\n\n{final_answer}", True)
 
1513
  if sent and not creds.get("is_premium"):
1514
  user_credits_db[str_chat_id]["file"] -= 1
1515
  save_db(user_credits_db)
 
1519
  else:
1520
  await send_with_keyboard(client, chat_id, "❌ تمامی سرورها شلوغ هستند. لطفاً بعداً امتحان کنید.", True)
1521
 
 
1522
  async def process_create_file(client, chat_id, topic):
1523
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1524
  creds = get_user_credits(str_chat_id)
 
1532
 
1533
  ai_prompt = f"یک مقاله بسیار جامع، کاملا حرفه‌ای، بسیار طولانی و با جزئیات کامل درباره موضوع زیر به زبان فارسی بنویس. فقط متن مقاله را بده و هیچ توضیح اضافه‌ای نده:\n\nموضوع: {topic}"
1534
 
 
1535
  article_text = None
1536
  for attempt in range(5):
1537
  keys_to_try = get_next_gemini_keys(100)
 
1587
  converter_url = "https://opera8-texttopdf.hf.space/"
1588
  uid = uuid.uuid4().hex
1589
 
 
 
 
1590
  pdf_success = False
1591
+ for attempt in range(30):
1592
  try:
1593
  pdf_bytes = None
1594
  async with aiohttp.ClientSession() as session:
 
1618
 
1619
  if res_upload is True:
1620
  pdf_success = True
1621
+ break
1622
  except Exception as e:
1623
  print(f"PDF error (attempt {attempt+1}):", e)
1624
 
1625
+ await asyncio.sleep(4)
1626
 
 
 
 
1627
  await asyncio.sleep(3.5)
1628
 
 
 
 
1629
  docx_success = False
1630
+ for attempt in range(30):
1631
  try:
1632
  docx_bytes = None
1633
  async with aiohttp.ClientSession() as session:
 
1657
 
1658
  if res_upload is True:
1659
  docx_success = True
1660
+ break
1661
  except Exception as e:
1662
  print(f"DOCX error (attempt {attempt+1}):", e)
1663
 
1664
+ await asyncio.sleep(4)
1665
 
1666
  try:
1667
  if proc_msg:
 
1670
  if msg_id: await client.delete_messages(chat_id,[msg_id])
1671
  except Exception: pass
1672
 
 
1673
  if pdf_success and docx_success:
1674
  if not creds.get("is_premium"):
1675
  user_credits_db[str_chat_id]["chat"] -= 1
 
1683
  else:
1684
  await send_with_keyboard(client, chat_id, "❌ متأسفانه پس از تلاش‌های مکرر، سرور قادر به ساخت و ارسال هیچ‌یک از فایل‌ها نبود و اعتباری از شما کسر نشد.", True)
1685
 
1686
+ # ==================================================================
1687
+ # حلقه و مدیریت اصلی ربات
1688
+ # ==================================================================
1689
  if not bot_token:
1690
  print("خطا: توکن ربات روبیکا وارد نشده است!")
1691
  else:
 
1693
 
1694
  @bot.on_update(filters.private)
1695
  async def main_handler(client, update):
1696
+ global BOT_GUID
1697
 
1698
  try:
1699
  if not BOT_GUID:
 
1720
  if not msg_id and msg_obj:
1721
  msg_id = msg_obj.get("message_id") if isinstance(msg_obj, dict) else getattr(msg_obj, "message_id", None)
1722
 
1723
+ str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1724
+ creds = get_user_credits(str_chat_id)
1725
+
1726
+ # --- سیستم فوق بهینه پیگیری پیام (Offset Tracking) ---
1727
+ if msg_id:
1728
+ try:
1729
+ current_msg_int = int(msg_id)
1730
+ last_msg_int = creds.get("last_msg_id", 0)
 
 
 
1731
 
1732
+ if current_msg_int <= last_msg_int:
1733
+ return
1734
+
1735
+ user_credits_db[str_chat_id]["last_msg_id"] = current_msg_int
1736
+ save_db(user_credits_db)
1737
+ except Exception:
1738
+ pass
1739
 
1740
  user_text = getattr(update, "text", "") or getattr(msg_obj, "text", "")
1741
  user_text_str = str(user_text).strip() if user_text else ""
1742
  user_text_lower = user_text_str.lower()
1743
 
 
 
 
1744
  if is_user_spamming(str_chat_id):
1745
  return
1746
 
1747
  if str_chat_id not in user_states:
1748
  user_states[str_chat_id] = {"mode": None, "text": "", "history":[], "file_bytes": None, "file_name": None}
1749
 
 
 
1750
  if user_text_lower.startswith(f"{ADMIN_CODE} pro=") or user_text_lower.startswith(f"{ADMIN_CODE}pro="):
1751
  parts = user_text_str.split("=", 1)
1752
  if len(parts) >= 2:
 
1764
  "tts": 5,
1765
  "file": 1,
1766
  "stt": 5,
1767
+ "voice_conv": 3,
1768
+ "voice_clone": 1,
1769
+ "last_msg_id": 0,
1770
  "has_joined": True,
1771
  "invited_count": 0,
1772
  "used_referral": False,
 
1784
  user_credits_db[target_id]["tts"] = 999999
1785
  user_credits_db[target_id]["file"] = 999999
1786
  user_credits_db[target_id]["stt"] = 999999
1787
+ user_credits_db[target_id]["voice_conv"] = 999999
1788
+ user_credits_db[target_id]["voice_clone"] = 999999
1789
 
1790
  save_db(user_credits_db)
1791
  await send_with_keyboard(client, chat_id, f"✅ حساب کاربر `{target_id}` به مدت ۳۰ روز شارژ شد و به پرو ارتقا یافت.", False)
 
1835
  status_text = "🥉 نسخه رایگان (آزمایشی)\n⏳ وضعیت: سهمیه روزانه محدود"
1836
 
1837
  chat_rem = "نامحدود ∞" if is_prem else t_creds.get('chat', 0)
1838
+ vc_rem = "نامحدود ∞" if is_prem else t_creds.get('voice_conv', 0)
1839
+ clone_rem = "نامحدود ∞" if is_prem else t_creds.get('voice_clone', 0)
1840
  podcast_rem = "نامحدود ∞" if is_prem else t_creds.get('podcast', 0)
1841
  tts_rem = "نامحدود ∞" if is_prem else t_creds.get('tts', 0)
1842
  file_rem = "نامحدود ∞" if is_prem else t_creds.get('file', 0)
 
1858
  💬 چت: {chat_rem}
1859
  🎨 تولید عکس: {image_rem}
1860
  🪄 ویرایش عکس: {edit_image_rem}
1861
+ 🎙️ تغییر صدا: {vc_rem}
1862
+ 👤 کلون کردن صدا: {clone_rem}
1863
  🎙 پادکست: {podcast_rem}
1864
  🗣 متن به صدا: {tts_rem}
1865
  📁 تحلیل فایل: {file_rem}
 
1944
  daily_note = "*نکته: سهمیه شما هر روز ساعت ۰۰:۰۰ بامداد به صورت خودکار مجدداً شارژ می‌گردد.*"
1945
 
1946
  chat_rem = "نامحدود ∞" if is_prem else creds['chat']
1947
+ vc_rem = "نامحدود ∞" if is_prem else creds.get('voice_conv', 0)
1948
+ clone_rem = "نامحدود ∞" if is_prem else creds.get('voice_clone', 0)
1949
  podcast_rem = "نامحدود ∞" if is_prem else creds['podcast']
1950
  tts_rem = "نامحدود ∞" if is_prem else creds['tts']
1951
  file_rem = "نامحدود ∞" if is_prem else creds['file']
 
1966
  - 💬 چت هوشمند: {chat_rem}
1967
  - 🎨 تولید تصویر: {image_rem} عدد
1968
  - 🪄 ویرایش تصویر: {edit_image_rem} عدد
1969
+ - 🎙️ تغییر صدا: {vc_rem}
1970
+ - 👤 کلون کردن صدا: {clone_rem}
1971
  - 🎙 ساخت پادکست: {podcast_rem}
1972
  - 🗣 تبدیل متن به صدا: {tts_rem}
1973
  - 📁 تحلیل فایل و سند: {file_rem}
 
1977
  await send_with_keyboard(client, chat_id, account_profile, True)
1978
  return
1979
 
 
 
 
1980
  if user_text_str in["/invite", "دعوت دوستان 🎁"]:
1981
  invites = creds.get("invited_count", 0)
1982
  remains = 10 - (invites % 10)
 
2004
  ✨ با این ربات می‌تونی کارهای زیر رو به راحتی انجام بدی:
2005
  💬 چت با پیشرفته‌ترین هوش مصنوعی
2006
  🎨 ساخت و ویرایش حرفه‌ای عکس
2007
+ 🎙️ تغییر صدا و کلون کردن صدا
2008
  🎙 ساخت پادکست اختصاصی
2009
  🗣 تبدیل متن به صدا (30 گوینده مختلف)
2010
  📝 تبدیل صدا و ویدیو به متن
 
2034
 
2035
  🎁 **بسته طلایی یک‌ماهه شامل:**
2036
  🤖 چت با هوش مصنوعی بصورت نامحدود
2037
+ 🎙️ تغییر صدا و کلون کردن صدای نامحدود
2038
  🗣 تبدیل متن به صدا بصورت نامحدود با ۳۰ گوینده
2039
  🎙 ساخت پادکست بصورت نامحدود
2040
 
 
2100
  await send_with_keyboard(client, chat_id, "📻 شما وارد بخش **ساخت پادکست** شدید.\n\nلطفاً موضوع پادکست خود را بفرستید.\nمثال: درباره تاریخچه پیدایش قهوه با ۳ گوینده یک پادکست جذاب بساز . همچنین این قسمت متصل به مدل زبانی است و درخواست هارو قبل از ساخت درک میکنه. میتوانید مقاله کامل یک سایت بفرستید با تبلیغات یا هرچی، هوش مصنوعی متن مقاله رو استخراج و پادکست براتون میسازه . در توضیحات امکان مشخص کردن تعداد گوینده به همراه اسم شون نیز از سمت شما امکان پذیر است.\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2101
  return
2102
 
2103
+ # ===============================================
2104
+ # بخش‌های جدید: تغییر صدا و کلون کردن صدا
2105
+ # ===============================================
2106
+ if user_text_str in["/vc", "تغییر صدا 🎙️"]:
2107
+ user_states[str_chat_id]["mode"] = "vc_waiting_for_voice"
2108
+ user_states[str_chat_id]["file_bytes"] = None
2109
+ await send_with_keyboard(client, chat_id, "🎙️ شما وارد بخش **تغییر صدا** شدید.\n\nلطفاً صدای خود (ویس) را بفرستید تا آن را به صدای شخصیت‌های معروف تبدیل کنم:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2110
+ return
2111
+
2112
+ if user_text_str in["/clone", "کلون کردن صدا 👤"]:
2113
+ user_states[str_chat_id]["mode"] = "clone_waiting_for_src"
2114
+ user_states[str_chat_id]["file_bytes"] = None
2115
+ user_states[str_chat_id]["ref_bytes"] = None
2116
+ await send_with_keyboard(client, chat_id, "👤 شما وارد بخش **کلون کردن صدای اختصاصی** شدید.\n\nدر این بخش شما به 2 فایل صوتی نیاز دارید:\n1️⃣ صدای خودتان (ورودی)\n2️⃣ صدای شخص هدف (الگو)\n\nابتدا **صدای خودتان (ورودی)** را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2117
+ return
2118
+ # ===============================================
2119
+
2120
  if user_text_str in["/file", "تحلیل فایل 📁"]:
2121
  user_states[str_chat_id]["mode"] = "file_waiting_for_file"
2122
  user_states[str_chat_id]["file_bytes"] = None
 
2140
  elif user_text_str: await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا از کیبورد پایین، بخش مورد نظرتان را انتخاب کنید:", True)
2141
  return
2142
 
 
 
 
2143
  elif current_mode == "waiting_for_referral_code":
2144
  if user_text_str:
2145
  normalized_code = to_english_digits(user_text_str).strip()
 
2207
  await send_with_keyboard(client, chat_id, "✅ کد هدیه با موفقیت ثبت شد!\n🎁 **10 سهمیه تبدیل رایگان متن به صدا** به حساب شما اضافه گردید.", True)
2208
  return
2209
 
 
 
 
2210
  elif current_mode == "chat":
2211
  if is_file:
2212
  await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
 
2233
  await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False)
2234
  return
2235
 
 
 
 
2236
  elif current_mode == "image_waiting_for_text":
2237
  if user_text_str:
2238
  user_states[str_chat_id]["text"] = user_text_str
 
2356
  await send_with_keyboard(client, chat_id, "⚠️ لطفاً موضوع مقاله خود را به صورت متنی بفرستید.", False)
2357
  return
2358
 
2359
+ # ===============================================
2360
+ # بخش‌های تغییر صدا و کلون صدا
2361
+ # ===============================================
2362
+ elif current_mode == "vc_waiting_for_voice":
2363
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً یک فایل صوتی یا ویس ارسال کنید.", False)
2364
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل صوتی شما...", False)
2365
+ try:
2366
+ file_bytes = await helper_download_file(client, msg_obj)
2367
+ user_states[str_chat_id]["file_bytes"] = file_bytes
2368
+ user_states[str_chat_id]["mode"] = "vc_waiting_for_model"
2369
+
2370
+ model_menu = "✅ صدای شما دریافت شد.\n\nلطفا **شماره** مدلی که می‌خواهید صدایتان به آن تبدیل شود را ارسال کنید:\n\n"
2371
+ for k, v in LEGACY_MODELS.items(): model_menu += f"{k}. {v['name']}\n"
2372
+ model_menu += "➖➖➖➖➖➖➖➖\n"
2373
+ for k, v in STANDARD_MODELS.items(): model_menu += f"{k}. {v['name']}\n"
2374
+
2375
+ await send_with_keyboard(client, chat_id, model_menu, False)
2376
+ except Exception as dl_err:
2377
+ await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
2378
+ return
2379
+
2380
+ elif current_mode == "vc_waiting_for_model":
2381
+ choice = to_english_digits(user_text_str).strip()
2382
+
2383
+ if choice in STANDARD_MODELS:
2384
+ if creds["voice_conv"] <= 0: return await send_with_keyboard(client, chat_id, "❌ سهمیه تغییر صدای شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
2385
+ user_states[str_chat_id]["mode"] = None
2386
+ model = STANDARD_MODELS[choice]
2387
+ src_bytes = user_states[str_chat_id]["file_bytes"]
2388
+ await send_with_keyboard(client, chat_id, "📥 در حال آماده‌سازی فایل‌ها...", False)
2389
+ ref_bytes = await helper_download_url_to_bytes(model["ref"])
2390
+ if not ref_bytes: return await send_with_keyboard(client, chat_id, "❌ خطا در دسترسی به فایل صدای مدل.", False)
2391
+
2392
+ asyncio.create_task(process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, model["name"], "voice_conv"))
2393
+
2394
+ elif choice in LEGACY_MODELS:
2395
+ if creds["voice_conv"] <= 0: return await send_with_keyboard(client, chat_id, "❌ سهمیه تغییر صدای شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
2396
+ user_states[str_chat_id]["selected_model"] = choice
2397
+ user_states[str_chat_id]["mode"] = "vc_waiting_for_gender"
2398
+
2399
+ gender_msg = f"✅ مدل {LEGACY_MODELS[choice]['name']} انتخاب شد.\n\nبرای تنظیم دقیق فرکانس‌ها به ما بگویید صدایی که خودتان فرستادید صدای یک **مرد** است یا **زن**؟\n\n1. مرد 👨\n2. زن 👩"
2400
+ await send_with_keyboard(client, chat_id, gender_msg, False)
2401
+ else:
2402
+ await send_with_keyboard(client, chat_id, "❌ شماره وارد شده صحیح نیست. لطفاً فقط عدد مدل را بفرستید.", False)
2403
+ return
2404
+
2405
+ elif current_mode == "vc_waiting_for_gender":
2406
+ choice = to_english_digits(user_text_str).strip()
2407
+ if choice not in ["1", "2"]:
2408
+ return await send_with_keyboard(client, chat_id, "❌ لطفاً عدد 1 (مرد) یا 2 (زن) را ارسال کنید.", False)
2409
+
2410
+ user_gender = "male" if choice == "1" else "female"
2411
+ model_key = user_states[str_chat_id]["selected_model"]
2412
+ model = LEGACY_MODELS[model_key]
2413
+ target_gender = model["gender"]
2414
+
2415
+ pitch = 0
2416
+ if target_gender == "female" and user_gender == "male": pitch = 12
2417
+ elif target_gender == "male" and user_gender == "female": pitch = -12
2418
+
2419
+ user_states[str_chat_id]["mode"] = None
2420
+ src_bytes = user_states[str_chat_id]["file_bytes"]
2421
+ asyncio.create_task(process_legacy_vc_job(client, chat_id, src_bytes, model["url"], pitch, model["name"]))
2422
+ return
2423
+
2424
+ elif current_mode == "clone_waiting_for_src":
2425
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً فایل صوتی صدای خودتان را بفرستید.", False)
2426
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود صدای شما...", False)
2427
+ try:
2428
+ file_bytes = await helper_download_file(client, msg_obj)
2429
+ user_states[str_chat_id]["file_bytes"] = file_bytes
2430
+ user_states[str_chat_id]["mode"] = "clone_waiting_for_ref"
2431
+ await send_with_keyboard(client, chat_id, "✅ صدای شما دریافت شد.\n\nحالا **فایل الگو (صدای شخصی که می‌خواهید شبیه‌سازی کنید)** را ارسال کنید.\n(پیشنهاد: فایل بدون نویز و موسیقی، بین ۳ تا ۱۰ ثانیه)", False)
2432
+ except Exception as dl_err:
2433
+ await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
2434
+ return
2435
+
2436
+ elif current_mode == "clone_waiting_for_ref":
2437
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً فایل صوتی الگوی هدف را بفرستید.", False)
2438
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود صدای الگو...", False)
2439
+ try:
2440
+ ref_bytes = await helper_download_file(client, msg_obj)
2441
+
2442
+ if creds["voice_clone"] <= 0:
2443
+ return await send_with_keyboard(client, chat_id, "❌ سهمیه کلون کردن صدای شما تمام شده است. نیازمند تهیه اشتراک هستید.", False)
2444
+
2445
+ user_states[str_chat_id]["mode"] = None
2446
+ src_bytes = user_states[str_chat_id]["file_bytes"]
2447
+
2448
+ asyncio.create_task(process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, "صدای اختصاصی (کلون شده)", "voice_clone"))
2449
+ except Exception as dl_err:
2450
+ await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
2451
+ return
2452
+
2453
  except Exception: traceback.print_exc()
2454
 
2455
  if __name__ == "__main__":
 
2459
  loop = asyncio.get_event_loop()
2460
  loop.set_default_executor(concurrent.futures.ThreadPoolExecutor(max_workers=32))
2461
 
 
2462
  loop.create_task(background_db_uploader())
2463
 
2464
+ print("ربات آلفا پرو با سیستم اشتراک نامحدود + دانلود پیشرفته + آپدیت دیتابیس (جلوگیری از ریست) + تغییر و کلون صدا روشن شد...")
2465
  bot.run()