Update main.py
Browse files
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("🚨 عملیات نجات جراحی
|
| 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 |
-
|
| 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: ساخت صدا از روی متن (تبدیل متن به صدا -
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1539 |
headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
|
| 1540 |
|
| 1541 |
audio_bytes = None
|
| 1542 |
last_error = "پاسخی دریافت نشد"
|
| 1543 |
|
| 1544 |
-
|
| 1545 |
-
|
| 1546 |
-
|
| 1547 |
-
|
| 1548 |
-
|
| 1549 |
-
|
| 1550 |
-
|
| 1551 |
-
|
| 1552 |
-
|
| 1553 |
-
|
| 1554 |
-
|
| 1555 |
-
|
| 1556 |
-
|
| 1557 |
-
|
| 1558 |
-
|
| 1559 |
-
|
| 1560 |
-
|
| 1561 |
-
|
| 1562 |
-
|
| 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 |
-
|
| 1573 |
-
|
|
|
|
| 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,
|
| 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(
|
|
|
|
| 1595 |
else:
|
| 1596 |
await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
|
| 1597 |
-
except Exception:
|
|
|
|
| 1598 |
|
| 1599 |
# ==============================================================================
|
| 1600 |
-
# 🟢 پارت 17: سیستم ساخت پادکست (ضد قطعی و
|
| 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, "📻 در حال بررسی موضوع و
|
| 1609 |
-
|
|
|
|
| 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 |
-
|
|
|
|
| 1615 |
payload_create = {"prompt": prompt, "available_speakers": available_speakers}
|
| 1616 |
|
| 1617 |
async with aiohttp.ClientSession() as session:
|
| 1618 |
task_id = None
|
| 1619 |
-
|
| 1620 |
-
|
|
|
|
| 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, "❌
|
| 1635 |
|
| 1636 |
-
|
| 1637 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1638 |
for _ in range(500):
|
| 1639 |
-
await asyncio.sleep(
|
| 1640 |
try:
|
| 1641 |
-
async with session.get(url_status) as resp:
|
| 1642 |
if resp.status == 200:
|
| 1643 |
status_data = await resp.json()
|
| 1644 |
-
|
| 1645 |
-
|
|
|
|
|
|
|
|
|
|
| 1646 |
break
|
| 1647 |
-
|
| 1648 |
-
|
|
|
|
|
|
|
|
|
|
| 1649 |
elif resp.status == 429:
|
| 1650 |
await asyncio.sleep(5)
|
| 1651 |
-
except Exception:
|
|
|
|
| 1652 |
|
| 1653 |
-
if not
|
|
|
|
| 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,
|
| 1663 |
|
| 1664 |
-
|
| 1665 |
-
|
|
|
|
| 1666 |
|
| 1667 |
-
for
|
| 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 |
-
|
| 1689 |
-
|
| 1690 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1691 |
|
|
|
|
| 1692 |
file_name_mp3 = f"final_podcast_{uuid.uuid4().hex}.mp3"
|
| 1693 |
-
await asyncio.to_thread(
|
| 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"🎧 فایل پادکست شما
|
| 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"❌ پادکست
|
| 1721 |
|
| 1722 |
-
if os.path.exists(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) و تحلیل انواع فایل با هوش مصنوعی
|