Opera8 commited on
Commit
fd4a564
·
verified ·
1 Parent(s): c66ec95

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +162 -87
main.py CHANGED
@@ -93,7 +93,7 @@ def gregorian_to_jalali(gy, gm, gd):
93
  return jy, jm, jd
94
 
95
  # ==============================================================================
96
- # 🟢 پارت 4: دیتابیس SQLite (نسخه نجات جراحی - پریدن از روی سکتورهای خراب)
97
  # ==============================================================================
98
  import os
99
  import sqlite3
@@ -101,9 +101,9 @@ import json
101
  import copy
102
  import threading
103
 
104
- # 🚀 انتقال به v6 برای انجام مجدد عملیات نجات (این بار به صورت خط‌به‌خط جراحی)
105
  DB_FILE = "/data/users_v6.db"
106
  OLD_DB_V3 = "/data/users_v3.db"
 
107
 
108
  last_saved_state = {}
109
  recent_messages_dict = {}
@@ -126,9 +126,13 @@ def init_sqlite_db():
126
  conn.commit()
127
 
128
  if is_first_run:
129
- print("🚨 عملیات نجات جراحی (دور زدن خرابی‌ها) از فایل v3 آغاز شد...")
130
  surgical_salvage(OLD_DB_V3, conn)
131
 
 
 
 
 
132
  conn.close()
133
  except Exception as e:
134
  print(f"❌ خطا در راه اندازی دیتابیس: {e}")
@@ -178,6 +182,59 @@ def surgical_salvage(old_file, new_conn):
178
  except Exception as e:
179
  print(f"⚠️ توقف استخراج: {e} | تعداد نجات‌یافته تا این لحظه: {extracted_count}")
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  def load_db():
182
  global last_saved_state, recent_messages_dict
183
  init_sqlite_db()
@@ -213,7 +270,7 @@ def background_save_worker(changed_data):
213
 
214
  def save_db(db_data):
215
  global last_saved_state
216
- changed =[]
217
  for cid, data in db_data.items():
218
  if cid not in last_saved_state or last_saved_state[cid] != data:
219
  changed.append((str(cid), json.dumps(data, ensure_ascii=False)))
@@ -761,12 +818,12 @@ SPEAKERS = {
761
  user_states = {}
762
 
763
  # ==============================================================================
764
- # 🟢 پارت 12: توابع تغییر صدا و لیست گویندگان
765
  # ==============================================================================
766
  # ==================================================================
767
  # لیست‌های اولیه ربات
768
  # ==================================================================
769
- WORKER_URLS =["https://opera8-ttspro.hf.space/generate"]
770
 
771
  SPEAKERS = {
772
  "1": ("شهاب (مرد)", "Charon"), "2": ("آوا (زن)", "Zephyr"), "3": ("نوید (مرد)", "Achird"),
@@ -795,7 +852,7 @@ async def process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, job_typ
795
  async with aiohttp.ClientSession() as session:
796
  job_id = None
797
  total_chunks = 1
798
- chunks =[]
799
 
800
  # ♻️ سیستم تلاش مجدد پنهان (دور زدن ارور 429)
801
  for attempt in range(8):
@@ -809,7 +866,7 @@ async def process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, job_typ
809
  data = await resp.json()
810
  job_id = data.get("job_id")
811
  total_chunks = data.get("total_chunks", 1)
812
- chunks = data.get("chunks",[])
813
  break
814
  elif resp.status == 429:
815
  await asyncio.sleep(4 + attempt * 2) # تاخیر تصاعدی تا سرور خلوت شود
@@ -834,7 +891,7 @@ async def process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, job_typ
834
  if c_data.get("status") == "completed":
835
  final_filename = c_data.get("filename")
836
  break
837
- elif c_data.get("status") in["failed", "error"]:
838
  return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش صدا.", True)
839
  elif c_resp.status == 429:
840
  await asyncio.sleep(5)
@@ -935,7 +992,7 @@ async def process_legacy_vc_job(client, chat_id, src_bytes, model_url, pitch, mo
935
  if c_data.get("status") == "completed":
936
  final_filename = c_data.get("filename")
937
  break
938
- elif c_data.get("status") in["failed", "error", "not_found"]:
939
  return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش.", True)
940
  elif c_resp.status == 429:
941
  await asyncio.sleep(5)
@@ -1127,7 +1184,7 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
1127
  # 🟢 تزریق شخصیت و دستورالعمل جدید به مدل
1128
  system_rules = """تو یک دستیار با مزه از برنامه هوش مصنوعی آلفا هستی😊 و توسط هوش مصنوعی آلفا توسعه داده شدی.
1129
  اینگونه می‌تونی خودت رو معرفی کنی، من یه هوش مصنوعیِ ساخته‌شده توسط تیم‌ تخصصی آلفا ام، و بر پایه مدل GPT-5.4 کار می‌کنم. یعنی یه نوع نرم‌افزار خیلی هوشمند که با کمک میلیون‌ها داده و آموزش‌های پیشرفته ساخته شده، تا بتونه بهت کمک کنه، سوالاتت رو جواب بده، یا حتی یه شوخی خنده‌دار برات بگه وقتی حال و هوات گرفته‌ست!
1130
- در واقع، من نتیجه سال‌ها تحقیقات و تلاش‌های مهندس‌ها و پژوهشگرها هستم، تا جایی که می‌تونم بهترین کمک رو بهت بکنم. این دستورات های تو هستند و بصورت رندوم متفاوت جواب بده و از شکلک های مناسب و جواب های جذاب استفاده کن. اگر در یک مکالمه اول سلام کردی در پیام های بعدی سلام نیاز نیست ."""
1131
 
1132
  # 🟢 استخراج تاریخچه و تبدیل آن به متنی پیوسته جهت تزریق مستقیم به حافظه مدل (تضمین ۱۰۰٪ یادآوری)
1133
  text_history = ""
@@ -1525,7 +1582,7 @@ async def process_image_edit(client, chat_id, image_bytes, prompt):
1525
  await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس ویرایش شده:\n{str(e)[:150]}", True)
1526
 
1527
  # ==============================================================================
1528
- # 🟢 پارت 16: ساخت صدا از روی متن (تبدیل متن به صدا - TTS)
1529
  # ==============================================================================
1530
  async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1531
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
@@ -1535,48 +1592,57 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1535
 
1536
  try:
1537
  proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا با «{speaker_name}»...\n(لطفاً صبور باشید)", False)
1538
- payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
 
 
 
 
 
 
 
 
1539
  headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
1540
 
1541
  audio_bytes = None
1542
  last_error = "پاسخی دریافت نشد"
1543
 
1544
- for attempt in range(10):
1545
- workers = WORKER_URLS.copy()
1546
- random.shuffle(workers)
1547
- async with aiohttp.ClientSession(headers=headers, timeout=aiohttp.ClientTimeout(total=600)) as session:
1548
- for worker_url in workers[:3]:
1549
- try:
1550
- async with session.post(worker_url, json=payload) as response:
1551
- if response.status == 200:
1552
- content_type = response.headers.get('Content-Type', '')
1553
- if 'audio' in content_type or response.content_length > 1000:
1554
- audio_bytes = await response.read()
1555
- break
1556
- else: last_error = "فایل نامعتبر"
1557
- else: last_error = f"ارور ({response.status})"
1558
- except Exception as e:
1559
- last_error = f"خطا: {str(e)}"
1560
- continue
1561
- if audio_bytes: break
1562
- await asyncio.sleep(2)
1563
 
1564
  try:
1565
  if proc_msg:
1566
  msg_id = getattr(proc_msg, 'message_id', None)
1567
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1568
- if msg_id: await client.delete_messages(chat_id,[msg_id])
1569
  except Exception: pass
1570
 
1571
  if audio_bytes:
1572
- file_name_mp3 = f"audio_{uuid.uuid4().hex}.mp3"
1573
- await asyncio.to_thread(sync_write_file, file_name_mp3, audio_bytes)
 
1574
  await asyncio.sleep(1)
1575
 
1576
  upload_result_file = False
1577
  error_log_tts = ""
1578
  for up_att in range(3):
1579
- res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", "✅ صدای شما با موفقیت آماده شد (فایل MP3):")
1580
  if res is True:
1581
  upload_result_file = True
1582
  break
@@ -1591,13 +1657,15 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1591
  else:
1592
  await send_with_keyboard(client, chat_id, f"❌ فایل صدا ساخته شد اما سرور روبیکا اجازه آپلود نداد:\n`{str(error_log_tts)[:800]}`", True)
1593
 
1594
- if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
 
1595
  else:
1596
  await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
1597
- except Exception: traceback.print_exc()
 
1598
 
1599
  # ==============================================================================
1600
- # 🟢 پارت 17: سیستم ساخت پادکست (ضد قطعی و مقاوم در برابر 429)
1601
  # ==============================================================================
1602
  async def process_podcast(client, chat_id, prompt):
1603
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
@@ -1605,19 +1673,22 @@ async def process_podcast(client, chat_id, prompt):
1605
  if creds["podcast"] <= 0:
1606
  return await send_with_keyboard(client, chat_id, "❌ اعتبار ساخت پادکست شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
1607
 
1608
- proc_msg = await send_with_keyboard(client, chat_id, "📻 در حال بررسی موضوع و نگارش سناریوی پادکست توسط هوش مصنوعی...\n(لطفاً صبور باشید)", False)
1609
- available_speakers =[]
 
1610
  for num_key, (spk_name, spk_id) in SPEAKERS.items():
1611
  gender = "male" if "مرد" in spk_name else "female"
1612
  available_speakers.append({"id": spk_id, "name": spk_name.split(' (')[0], "gender": gender})
1613
 
1614
- url_create = "https://opera8-podgen.hf.space/api/create-full-podcast"
 
1615
  payload_create = {"prompt": prompt, "available_speakers": available_speakers}
1616
 
1617
  async with aiohttp.ClientSession() as session:
1618
  task_id = None
1619
- # ♻️ تلاش مجدد برای جلوگیری از شکست در صورت شلوغی سرور
1620
- for attempt in range(8):
 
1621
  try:
1622
  async with session.post(url_create, json=payload_create, timeout=60) as resp:
1623
  if resp.status == 202:
@@ -1631,75 +1702,78 @@ async def process_podcast(client, chat_id, prompt):
1631
  await asyncio.sleep(3)
1632
 
1633
  if not task_id:
1634
- return await send_with_keyboard(client, chat_id, "❌ ترافیک سرور پادکست بالا است (خطای 429). لطفاً چند دقیقه دیگر امتحان کنید.", True)
1635
 
1636
- url_status = f"https://opera8-podgen.hf.space/api/podcast-status/{task_id}"
1637
- script_data = None
 
 
 
 
1638
  for _ in range(500):
1639
- await asyncio.sleep(3)
1640
  try:
1641
- async with session.get(url_status) as resp:
1642
  if resp.status == 200:
1643
  status_data = await resp.json()
1644
- if status_data.get("status") == "completed":
1645
- script_data = status_data.get("data", {}).get("script",[])
 
 
 
1646
  break
1647
- elif status_data.get("status") == "failed":
1648
- return await send_with_keyboard(client, chat_id, "❌ متأسفانه هوش مصنوعی نتوانست برای این موضوع سناریو بنویسد.", True)
 
 
 
1649
  elif resp.status == 429:
1650
  await asyncio.sleep(5)
1651
- except Exception: pass
 
1652
 
1653
- if not script_data: return await send_with_keyboard(client, chat_id, "❌ زمان انتظار برای نوشتن سناریو به پایان رسید.", True)
 
1654
 
1655
  try:
1656
  if proc_msg:
1657
  msg_id = getattr(proc_msg, 'message_id', None)
1658
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1659
- if msg_id: await client.delete_messages(chat_id,[msg_id])
1660
  except: pass
1661
 
1662
- proc_msg = await send_with_keyboard(client, chat_id, f"🎙 سناریو نوشته شد! در حال ریکورد و میکس دیالوگ‌های گویندگان امل {len(script_data)} نوبت صحبت)...\nاین مرحله زمان‌بر است...", False)
1663
 
1664
- combined_audio = AudioSegment.empty()
1665
- url_generate = "https://opera8-podgen.hf.space/api/generate"
 
1666
 
1667
- for index, turn in enumerate(script_data):
1668
- payload_gen = {"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}
1669
- chunk_audio_bytes = None
1670
-
1671
- # ♻️ حلقه ضد قطعی برای ریکورد تک تک دیالوگ ها
1672
- for attempt in range(15):
1673
- try:
1674
- async with session.post(url_generate, json=payload_gen, timeout=120) as resp:
1675
- if resp.status == 200:
1676
- chunk_audio_bytes = await resp.read()
1677
- break
1678
- elif resp.status == 429:
1679
- await asyncio.sleep(4 + attempt * 2)
1680
- else:
1681
- await asyncio.sleep(2)
1682
- except Exception: await asyncio.sleep(2)
1683
-
1684
- if not chunk_audio_bytes:
1685
- return await send_with_keyboard(client, chat_id, f"❌ خطا در تولید صدای بخش {index+1} به دلیل شلوغی سرور. عملیات متوقف شد.", True)
1686
-
1687
  try:
1688
- combined_audio = await asyncio.to_thread(sync_combine_audio, combined_audio, chunk_audio_bytes)
1689
- except Exception as e:
1690
- return await send_with_keyboard(client, chat_id, f"❌ خطا در پردازش صدا:\n{str(e)}", True)
 
 
 
 
 
 
 
 
1691
 
 
1692
  file_name_mp3 = f"final_podcast_{uuid.uuid4().hex}.mp3"
1693
- await asyncio.to_thread(sync_export_audio, combined_audio, file_name_mp3)
1694
 
1695
  try:
1696
  if proc_msg:
1697
  msg_id = getattr(proc_msg, 'message_id', None)
1698
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1699
- if msg_id: await client.delete_messages(chat_id,[msg_id])
1700
  except: pass
1701
 
1702
- caption_file = f"🎧 فایل پادکست شما با فرمت MP3:\n\n💡 موضوع شما: {prompt}"
1703
 
1704
  upload_result_file = False
1705
  error_log_pod = ""
@@ -1717,9 +1791,10 @@ async def process_podcast(client, chat_id, prompt):
1717
  user_credits_db[str_chat_id]["podcast"] -= 1
1718
  save_db(user_credits_db)
1719
  else:
1720
- await send_with_keyboard(client, chat_id, f"❌ پادکست ساخته شد اما روبیکا پس از ده‌ها تلاش خطای آپلود داد.\n\n`{str(error_log_pod)[:800]}`", True)
1721
 
1722
- if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
 
1723
 
1724
  # ==============================================================================
1725
  # 🟢 پارت 18: استخراج متن از صدا (STT) و تحلیل انواع فایل با هوش مصنوعی
 
93
  return jy, jm, jd
94
 
95
  # ==============================================================================
96
+ # 🟢 پارت 4: دیتابیس SQLite (نسخه جراحی + بازیابی هوشمند کاربران ویژه از JSON قدیمی)
97
  # ==============================================================================
98
  import os
99
  import sqlite3
 
101
  import copy
102
  import threading
103
 
 
104
  DB_FILE = "/data/users_v6.db"
105
  OLD_DB_V3 = "/data/users_v3.db"
106
+ OLD_JSON_BAK = "Users_db.json.bak" # نام فایل بکاپ جیسون شما
107
 
108
  last_saved_state = {}
109
  recent_messages_dict = {}
 
126
  conn.commit()
127
 
128
  if is_first_run:
129
+ print("🚨 عملیات نجات جراحی از فایل v3 آغاز شد...")
130
  surgical_salvage(OLD_DB_V3, conn)
131
 
132
+ # 🟢 بازگردانی کاربران دارای اشتراک از فایل جیسون قدیمی
133
+ # این تابع همیشه چک می‌کند تا اگر کاربر ویژه‌ای جا مانده بود یا اشتراکش در دیتابیس فعلی پریده بود، آن را برگرداند.
134
+ salvage_premium_from_json(OLD_JSON_BAK, conn)
135
+
136
  conn.close()
137
  except Exception as e:
138
  print(f"❌ خطا در راه اندازی دیتابیس: {e}")
 
182
  except Exception as e:
183
  print(f"⚠️ توقف استخراج: {e} | تعداد نجات‌یافته تا این لحظه: {extracted_count}")
184
 
185
+ # 💎 تابع ارتقا یافته: بازیابی کاربران دارای اشتراک از فایل جیسون
186
+ def salvage_premium_from_json(json_file, new_conn):
187
+ # بررسی وجود فایل در مسیر فعلی یا پوشه data
188
+ target_path = json_file
189
+ if not os.path.exists(target_path):
190
+ alt_path = f"/data/{json_file}"
191
+ if os.path.exists(alt_path):
192
+ target_path = alt_path
193
+ else:
194
+ return # اگر فایل بکاپ کلا وجود نداشت، بی‌صدا رد می‌شود
195
+
196
+ print(f"📦 در حال اسکن فایل بکاپ {target_path} جهت یافتن کاربران VIP جا مانده یا فاقد اشتراک...")
197
+ try:
198
+ with open(target_path, 'r', encoding='utf-8') as f:
199
+ old_json_data = json.load(f)
200
+
201
+ restored_count = 0
202
+ cursor = new_conn.cursor()
203
+
204
+ for chat_id, user_data in old_json_data.items():
205
+ # فقط کاربرانی که دیتای آنها به صورت دیکشنری است و اشتراک فعال دارند را انتخاب می‌کنیم
206
+ if isinstance(user_data, dict) and user_data.get("is_premium") == True:
207
+ user_data_str = json.dumps(user_data, ensure_ascii=False)
208
+ str_chat_id = str(chat_id)
209
+ try:
210
+ # بررسی می‌کنیم آیا کاربر در دیتابیس فعلی (v6) وجود دارد یا خیر
211
+ cursor.execute("SELECT user_data FROM users WHERE chat_id = ?", (str_chat_id,))
212
+ existing_row = cursor.fetchone()
213
+
214
+ if existing_row:
215
+ # کاربر وجود دارد. بررسی می‌کنیم آیا اشتراکش فعال است؟
216
+ existing_data = json.loads(existing_row[0])
217
+ if not existing_data.get("is_premium"):
218
+ # اگر در دیتابیس فعلی اشتراک نداشت، دیتای پولی جایگزین می‌شود
219
+ cursor.execute("UPDATE users SET user_data = ? WHERE chat_id = ?", (user_data_str, str_chat_id))
220
+ restored_count += 1
221
+ else:
222
+ # کاربر اصلا در دیتابیس وجود ندارد، او را اضافه می‌کنیم
223
+ cursor.execute("INSERT INTO users (chat_id, user_data) VALUES (?, ?)", (str_chat_id, user_data_str))
224
+ restored_count += 1
225
+
226
+ except Exception:
227
+ continue
228
+
229
+ new_conn.commit()
230
+ if restored_count > 0:
231
+ print(f"💎 فوق‌العاده! اطلاعات {restored_count} کاربر دارای اشتراک (که غایب بودند یا در نسخه جدید اشتراک نداشتند) با موفقیت ریکاوری شد.")
232
+ else:
233
+ print("💎 ��ایل بکاپ بررسی شد؛ تمام کاربران VIP از قبل با اشتراک فعال در دیتابیس فعلی موجود هستند.")
234
+
235
+ except Exception as e:
236
+ print(f"❌ خطا در پردازش فایل جیسون بکاپ: {e}")
237
+
238
  def load_db():
239
  global last_saved_state, recent_messages_dict
240
  init_sqlite_db()
 
270
 
271
  def save_db(db_data):
272
  global last_saved_state
273
+ changed = []
274
  for cid, data in db_data.items():
275
  if cid not in last_saved_state or last_saved_state[cid] != data:
276
  changed.append((str(cid), json.dumps(data, ensure_ascii=False)))
 
818
  user_states = {}
819
 
820
  # ==============================================================================
821
+ # 🟢 پارت 12: لیست گویندگان و توابع تغییر صدا
822
  # ==============================================================================
823
  # ==================================================================
824
  # لیست‌های اولیه ربات
825
  # ==================================================================
826
+ # (آدرس کارگرها طبق درخواست حذف شد و پردازش به اسپیس پادکست منتقل گردید)
827
 
828
  SPEAKERS = {
829
  "1": ("شهاب (مرد)", "Charon"), "2": ("آوا (زن)", "Zephyr"), "3": ("نوید (مرد)", "Achird"),
 
852
  async with aiohttp.ClientSession() as session:
853
  job_id = None
854
  total_chunks = 1
855
+ chunks = []
856
 
857
  # ♻️ سیستم تلاش مجدد پنهان (دور زدن ارور 429)
858
  for attempt in range(8):
 
866
  data = await resp.json()
867
  job_id = data.get("job_id")
868
  total_chunks = data.get("total_chunks", 1)
869
+ chunks = data.get("chunks", [])
870
  break
871
  elif resp.status == 429:
872
  await asyncio.sleep(4 + attempt * 2) # تاخیر تصاعدی تا سرور خلوت شود
 
891
  if c_data.get("status") == "completed":
892
  final_filename = c_data.get("filename")
893
  break
894
+ elif c_data.get("status") in ["failed", "error"]:
895
  return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش صدا.", True)
896
  elif c_resp.status == 429:
897
  await asyncio.sleep(5)
 
992
  if c_data.get("status") == "completed":
993
  final_filename = c_data.get("filename")
994
  break
995
+ elif c_data.get("status") in ["failed", "error", "not_found"]:
996
  return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش.", True)
997
  elif c_resp.status == 429:
998
  await asyncio.sleep(5)
 
1184
  # 🟢 تزریق شخصیت و دستورالعمل جدید به مدل
1185
  system_rules = """تو یک دستیار با مزه از برنامه هوش مصنوعی آلفا هستی😊 و توسط هوش مصنوعی آلفا توسعه داده شدی.
1186
  اینگونه می‌تونی خودت رو معرفی کنی، من یه هوش مصنوعیِ ساخته‌شده توسط تیم‌ تخصصی آلفا ام، و بر پایه مدل GPT-5.4 کار می‌کنم. یعنی یه نوع نرم‌افزار خیلی هوشمند که با کمک میلیون‌ها داده و آموزش‌های پیشرفته ساخته شده، تا بتونه بهت کمک کنه، سوالاتت رو جواب بده، یا حتی یه شوخی خنده‌دار برات بگه وقتی حال و هوات گرفته‌ست!
1187
+ در واقع، من نتیجه سال‌ها تحقیقات و تلاش‌های مهندس‌ها و پژوهشگرها هستم، تا جایی که می‌تونم بهترین کمک رو بهت بکنم. این دستورات های تو هستند و بصورت رندوم متفاوت جواب بده و از شکلک های مناسب و جواب های جذاب استفاده کن. اگر در یک مکالمه اول سلام کردی در پیام های بعدی سلام نیاز نیست.."""
1188
 
1189
  # 🟢 استخراج تاریخچه و تبدیل آن به متنی پیوسته جهت تزریق مستقیم به حافظه مدل (تضمین ۱۰۰٪ یادآوری)
1190
  text_history = ""
 
1582
  await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس ویرایش شده:\n{str(e)[:150]}", True)
1583
 
1584
  # ==============================================================================
1585
+ # 🟢 پارت 16: ساخت صدا از روی متن (تبدیل متن به صدا - متصل به اسپیس پادکست)
1586
  # ==============================================================================
1587
  async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1588
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
 
1592
 
1593
  try:
1594
  proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا با «{speaker_name}»...\n(لطفاً صبور باشید)", False)
1595
+
1596
+ # اتصال مستقیم به اسپیس ساخت پادکست جهت تولید صدا
1597
+ tts_url = "https://opera8-podgen.hf.space/api/generate"
1598
+ payload = {
1599
+ "text": user_text,
1600
+ "speaker": speaker_id,
1601
+ "temperature": 0.9,
1602
+ "is_custom": False
1603
+ }
1604
  headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
1605
 
1606
  audio_bytes = None
1607
  last_error = "پاسخی دریافت نشد"
1608
 
1609
+ async with aiohttp.ClientSession(headers=headers, timeout=aiohttp.ClientTimeout(total=300)) as session:
1610
+ for attempt in range(6):
1611
+ try:
1612
+ async with session.post(tts_url, json=payload) as response:
1613
+ if response.status == 200:
1614
+ content_type = response.headers.get('Content-Type', '')
1615
+ if 'audio' in content_type or response.content_length > 1000:
1616
+ audio_bytes = await response.read()
1617
+ break
1618
+ else:
1619
+ last_error = "فایل نامعتبر"
1620
+ elif response.status == 429:
1621
+ await asyncio.sleep(4 + attempt * 2)
1622
+ else:
1623
+ last_error = f"ارور ({response.status})"
1624
+ await asyncio.sleep(2)
1625
+ except Exception as e:
1626
+ last_error = f"خطا: {str(e)}"
1627
+ await asyncio.sleep(2)
1628
 
1629
  try:
1630
  if proc_msg:
1631
  msg_id = getattr(proc_msg, 'message_id', None)
1632
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1633
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
1634
  except Exception: pass
1635
 
1636
  if audio_bytes:
1637
+ # ذخیره با فرمت wav (چون سرور فایل wav برمی‌گرداند)
1638
+ file_name_audio = f"audio_{uuid.uuid4().hex}.wav"
1639
+ await asyncio.to_thread(sync_write_file, file_name_audio, audio_bytes)
1640
  await asyncio.sleep(1)
1641
 
1642
  upload_result_file = False
1643
  error_log_tts = ""
1644
  for up_att in range(3):
1645
+ res = await helper_upload_file(client, chat_id, file_name_audio, "Music", "✅ صدای شما با موفقیت آماده شد:")
1646
  if res is True:
1647
  upload_result_file = True
1648
  break
 
1657
  else:
1658
  await send_with_keyboard(client, chat_id, f"❌ فایل صدا ساخته شد اما سرور روبیکا اجازه آپلود نداد:\n`{str(error_log_tts)[:800]}`", True)
1659
 
1660
+ if os.path.exists(file_name_audio):
1661
+ os.remove(file_name_audio)
1662
  else:
1663
  await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
1664
+ except Exception:
1665
+ traceback.print_exc()
1666
 
1667
  # ==============================================================================
1668
+ # 🟢 پارت 17: سیستم ساخت پادکست (ضد قطعی، بدون 429 و متصل به API هوشمند)
1669
  # ==============================================================================
1670
  async def process_podcast(client, chat_id, prompt):
1671
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
 
1673
  if creds["podcast"] <= 0:
1674
  return await send_with_keyboard(client, chat_id, "❌ اعتبار ساخت پادکست شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
1675
 
1676
+ proc_msg = await send_with_keyboard(client, chat_id, "📻 در حال بررسی موضوع و شروع ساخت پادکست در سرور پردازشی...\n(با توجه به طولانی بودن این فرآیند، لطفاً چند دقیقه صبور باشید. ربات در حال انجام عملیات است)", False)
1677
+
1678
+ available_speakers = []
1679
  for num_key, (spk_name, spk_id) in SPEAKERS.items():
1680
  gender = "male" if "مرد" in spk_name else "female"
1681
  available_speakers.append({"id": spk_id, "name": spk_name.split(' (')[0], "gender": gender})
1682
 
1683
+ # استفاده از API جدید که تمام کارها را صفر تا صد سمت اسپیس انجام می‌دهد
1684
+ url_create = "https://opera8-podgen.hf.space/api/auto-podcast"
1685
  payload_create = {"prompt": prompt, "available_speakers": available_speakers}
1686
 
1687
  async with aiohttp.ClientSession() as session:
1688
  task_id = None
1689
+
1690
+ # 1. ارسال درخواست اولیه به اسپیس
1691
+ for attempt in range(5):
1692
  try:
1693
  async with session.post(url_create, json=payload_create, timeout=60) as resp:
1694
  if resp.status == 202:
 
1702
  await asyncio.sleep(3)
1703
 
1704
  if not task_id:
1705
+ return await send_with_keyboard(client, chat_id, "❌ ارتباط با سرور پادکست در حال حاضر برقرار نشد. لطفاً چند دقیقه دیگر امتحان کنید.", True)
1706
 
1707
+ # 2. بررسی وضعیت پردازش در پس‌زمینه سرور اسپیس
1708
+ url_status = f"https://opera8-podgen.hf.space/api/auto-podcast-status/{task_id}"
1709
+ final_filename = None
1710
+ last_progress_message = ""
1711
+
1712
+ # ربات تا 1500 ثانیه (25 دقیقه) منتظر اتمام ساخت پادکست می‌ماند
1713
  for _ in range(500):
1714
+ await asyncio.sleep(4)
1715
  try:
1716
+ async with session.get(url_status, timeout=20) as resp:
1717
  if resp.status == 200:
1718
  status_data = await resp.json()
1719
+ current_status = status_data.get("status")
1720
+ progress_msg = status_data.get("progress", "")
1721
+
1722
+ if current_status == "completed":
1723
+ final_filename = status_data.get("filename")
1724
  break
1725
+
1726
+ elif current_status == "failed":
1727
+ error_detail = status_data.get("error", "نامشخص")
1728
+ return await send_with_keyboard(client, chat_id, f"❌ سرور در ساخت پادکست با خطا مواجه شد.\nدلیل: {error_detail}", True)
1729
+
1730
  elif resp.status == 429:
1731
  await asyncio.sleep(5)
1732
+ except Exception:
1733
+ pass
1734
 
1735
+ if not final_filename:
1736
+ return await send_with_keyboard(client, chat_id, "❌ زمان انتظار برای ساخت پادکست به پایان رسید و سرور پاسخ نهایی را نداد.", True)
1737
 
1738
  try:
1739
  if proc_msg:
1740
  msg_id = getattr(proc_msg, 'message_id', None)
1741
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1742
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
1743
  except: pass
1744
 
1745
+ proc_msg = await send_with_keyboard(client, chat_id, "📥 پادکست ساخته شد! در حال دانلود فایل نهایی از سرور و آماده‌سازی جهت ارسال...", False)
1746
 
1747
+ # 3. دانلود مستقیم فایل MP3 ترکیب شده از اسپیس
1748
+ download_url = f"https://opera8-podgen.hf.space/api/download-podcast/{final_filename}"
1749
+ audio_bytes = None
1750
 
1751
+ for attempt in range(5):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1752
  try:
1753
+ async with session.get(download_url, timeout=300) as resp:
1754
+ if resp.status == 200:
1755
+ audio_bytes = await resp.read()
1756
+ break
1757
+ else:
1758
+ await asyncio.sleep(4)
1759
+ except Exception:
1760
+ await asyncio.sleep(4)
1761
+
1762
+ if not audio_bytes:
1763
+ return await send_with_keyboard(client, chat_id, "❌ فایل پادکست آماده شد اما ربات نتوانست آن را از سرور دانلود کند.", True)
1764
 
1765
+ # 4. ذخیره محلی موقت و ارسال به روبیکا
1766
  file_name_mp3 = f"final_podcast_{uuid.uuid4().hex}.mp3"
1767
+ await asyncio.to_thread(sync_write_file, file_name_mp3, audio_bytes)
1768
 
1769
  try:
1770
  if proc_msg:
1771
  msg_id = getattr(proc_msg, 'message_id', None)
1772
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1773
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
1774
  except: pass
1775
 
1776
+ caption_file = f"🎧 فایل پادکست شما آماده است:\n\n💡 موضوع شما: {prompt}"
1777
 
1778
  upload_result_file = False
1779
  error_log_pod = ""
 
1791
  user_credits_db[str_chat_id]["podcast"] -= 1
1792
  save_db(user_credits_db)
1793
  else:
1794
+ await send_with_keyboard(client, chat_id, f"❌ پادکست دانلود شد اما روبیکا خطای آپلود داد.\n\n`{str(error_log_pod)[:800]}`", True)
1795
 
1796
+ if os.path.exists(file_name_mp3):
1797
+ os.remove(file_name_mp3)
1798
 
1799
  # ==============================================================================
1800
  # 🟢 پارت 18: استخراج متن از صدا (STT) و تحلیل انواع فایل با هوش مصنوعی