diff --git "a/main.py" "b/main.py" --- "a/main.py" +++ "b/main.py" @@ -311,99 +311,16 @@ def to_english_digits(text): return str(text).translate(translation_table) # ============================================================================== -# 🟢 پارت 6: تنظیمات وب‌سرور (Flask)، دریافت وب‌هوک و توابع کمکی فایل‌ها +# 🟢 پارت 6: تنظیمات وب‌سرور (Flask) و توابع کمکی فایل‌ها # ============================================================================== -import requests -import asyncio -from flask import Flask, request, jsonify - -# --- تنظیمات وب سرور و وب‌هوک --- +# --- تنظیمات وب سرور --- app = Flask(__name__) -# آدرس وب‌هوک (اگر از Cloudflare Worker استفاده کردید، آدرس آن را اینجا بگذارید) -WEBHOOK_URL = "https://opera8-rubikabot.hf.space/" - -def set_webhook_on_rubika(): - bot_token = os.environ.get("RUBIKA_AUTH", "").strip() - if not bot_token: - print("❌ توکن روبیکا یافت نشد!") - return - - url = f"https://botapi.rubika.ir/v3/{bot_token}/updateBotEndpoints" - - payload_update = {"url": WEBHOOK_URL, "type": "ReceiveUpdate"} - payload_inline = {"url": WEBHOOK_URL, "type": "ReceiveInlineMessage"} - - try: - r1 = requests.post(url, json=payload_update, timeout=15) - print("🟢 Webhook Setup (ReceiveUpdate):", r1.json()) - - r2 = requests.post(url, json=payload_inline, timeout=15) - print("🟢 Webhook Setup (InlineMessage):", r2.json()) - except Exception as e: - print("❌ Webhook Setup Failed:", e) - -class UpdateWrapper: - def __init__(self, d): - self._d = d - for k, v in d.items(): - if isinstance(v, dict): - setattr(self, k, UpdateWrapper(v)) - elif isinstance(v, list): - setattr(self, k, [UpdateWrapper(i) if isinstance(i, dict) else i for i in v]) - else: - setattr(self, k, v) - - # 🟢 جادوی تطبیق متغیرهای وب‌هوک با استانداردهای ربات شما (رفع باگ خوانده نشدن پیام‌ها) - if hasattr(self, 'new_message'): - self.message = self.new_message - - if hasattr(self, 'message') and isinstance(self.message, UpdateWrapper): - if hasattr(self.message, 'sender_id') and not hasattr(self.message, 'author_object_guid'): - self.message.author_object_guid = self.message.sender_id - - if hasattr(self, 'chat_id') and not hasattr(self, 'object_guid'): - self.object_guid = self.chat_id - self.author_guid = self.chat_id - - def to_dict(self): - return self._d - - def get(self, key, default=None): - return getattr(self, key, default) - -@app.route('/', methods=['GET', 'POST']) +@app.route('/') def home(): - if request.method == 'GET': - return "ربات یکپارچه آلفا (نسخه پرو + وب‌هوک روبیکا) روشن است! 🚀" - - if request.method == 'POST': - try: - data = request.json - if data: - inner_data = data - if "update" in data: - inner_data = data["update"] - elif "inline_message" in data: - inner_data = data["inline_message"] - - wrapped_update = UpdateWrapper(inner_data) - - # تسک به سرعت به حلقه اصلی پاس داده می‌شود تا سرور منتظر نماند - if 'loop' in globals() and 'main_handler' in globals(): - asyncio.run_coroutine_threadsafe(main_handler(bot, wrapped_update), loop) - - except Exception as e: - print("❌ Error in webhook processing:", e) - - # همیشه و در کسری از ثانیه 200 OK برمی‌گردانیم تا روبیکا ارتباط را قطع نکند - return jsonify({"status": "ok"}) + return "ربات یکپارچه آلفا (نسخه پرو + مدیریت اشتراک نامحدود + دعوت دوستان + سیستم تغییر صدا) روشن است! 🚀" def run_flask(): - set_webhook_on_rubika() - import logging - log = logging.getLogger('werkzeug') - log.setLevel(logging.ERROR) app.run(host="0.0.0.0", port=7860, threaded=True) # --- توابع کمکی --- @@ -420,17 +337,12 @@ def sync_read_file(file_name): return f.read() def sync_combine_audio(current_audio, new_bytes): - from pydub import AudioSegment - import io audio_segment = AudioSegment.from_file(io.BytesIO(new_bytes)) return current_audio + audio_segment def sync_export_audio(audio_obj, file_name): audio_obj.export(file_name, format="mp3") - - - # ============================================================================== # 🟢 پارت 7: کیبوردها (دکمه���های شیشه‌ای / منوها) # ============================================================================== @@ -698,160 +610,171 @@ HF_TOKENS_STR = os.environ.get("HF_TOKENS", "") HF_TOKENS =[k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()] # ============================================================================== -# 🟢 پارت 11: سیستم آپلود ابری نوین (استفاده از سرورهای بله به عنوان S3 پرسرعت) +# 🟢 پارت 11: آپلودر قدرتمند و تابع ارسال فایل ضد خطا (نسخه نهایی ضد بمب اتم 🚀) # ============================================================================== -import asyncio -import os -import aiohttp - -# توکن ربات تستی بله شما (آپلودسنتر) -BALE_TOKEN = "2030030646:ioHMqrCsw0PwK5hTg_1Av1rsXZMiu-LETBI" -# در پیام‌رسان‌های تلگرام و بله، آیدی عددی ربات همان بخش اول توکن است -BALE_CHAT_ID = BALE_TOKEN.split(":")[0] - -# 🟢 ۱. مدیریت صف آپلود ابری -_upload_lock = None -_shared_session = None - -def get_upload_lock(): - global _upload_lock - if _upload_lock is None: - # ظرفیت روی 20 تنظیم شد (ارسال موشکی و بدون ارور) - _upload_lock = asyncio.Semaphore(20) - return _upload_lock - -async def get_shared_session(): - global _shared_session - if _shared_session is None or _shared_session.closed: - connector = aiohttp.TCPConnector(limit=100, keepalive_timeout=60) - _shared_session = aiohttp.ClientSession(connector=connector) - return _shared_session - async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""): abs_path = os.path.abspath(file_name) - if not os.path.exists(abs_path): - return "File Not Found" - - try: - file_bytes = await asyncio.to_thread(sync_read_file, abs_path) - except Exception as e: - return f"File Read Error: {e}" - error_logs = [] - rubika_bot_token = os.environ.get("RUBIKA_AUTH", "").strip() - - async with get_upload_lock(): - session = await get_shared_session() - download_link = None + + api_file_type = "Image" if file_type in["photo", "Image", "image"] else "Voice" if file_type in ["voice", "Voice", "audio"] else file_type + + # روبیکا فرمت‌های خاصی را قبول می‌کند. + if api_file_type == "Voice" and not abs_path.lower().endswith('.ogg'): + api_file_type = "Music" + if api_file_type == "File" and abs_path.lower().endswith('.mp3'): + api_file_type = "Music" - # 🚀 فاز اول: آپلود فایل به سرورهای بله و دریافت لینک مستقیم - for attempt in range(3): - try: - # ارسال فایل به چت خودِ ربات در بله - form = aiohttp.FormData() - form.add_field('chat_id', BALE_CHAT_ID) - form.add_field('document', file_bytes, filename=os.path.basename(abs_path)) - - bale_upload_url = f"https://tapi.bale.ai/bot{BALE_TOKEN}/sendDocument" - async with session.post(bale_upload_url, data=form, timeout=aiohttp.ClientTimeout(total=60)) as resp: - if resp.status == 200: - b_data = await resp.json() - file_id = b_data.get('result', {}).get('document', {}).get('file_id') - - if file_id: - # درخواست گرفتن مسیر فایل از سرور بله - bale_file_url = f"https://tapi.bale.ai/bot{BALE_TOKEN}/getFile?file_id={file_id}" - async with session.get(bale_file_url) as f_resp: - if f_resp.status == 200: - f_data = await f_resp.json() - file_path = f_data.get('result', {}).get('file_path') - if file_path: - # ساخت لینک دانلود مستقیم نهایی - download_link = f"https://tapi.bale.ai/file/bot{BALE_TOKEN}/{file_path}" - break - except Exception as e: - error_logs.append(f"Bale Upload Error (Attempt {attempt+1}): {str(e)[:30]}") - await asyncio.sleep(2) - - # 🚀 فاز دوم: ارسال پیام متنی زیبا به کاربر در روبیکا - if download_link: - # 🟢 دیزاین جدید پیام با خطوط جداکننده و توضیحات کامل - final_caption = f"""{caption} - -➖➖➖➖➖➖➖➖➖➖ -🌐 **لینک دانلود مستقیم فایل:** -`{download_link}` -➖➖➖➖➖➖➖➖➖➖ - -⚠️ **راهنمای دانلود:** -لطفاً لینک بالا را کپی کرده و در مرورگر گوشی خود (مثل کروم، فایرفاکس یا سافاری) باز کنید تا فایلتان دانلود شود. (چون روبیکا اجازه نمی‌دهد، مستقیماً از اینجا روی لینک کلیک نکنید). - -🇮🇷 این لینک روی دامنه سایت پیام‌رسان داخلی «بله» قرار دارد و به راحتی با اینترنت ملی (بدون فیلترشکن) با بالاترین سرعت دانلود می‌شود. - -⏳ *پ.ن: در صورتی که محدودیت‌های روبیکا در دریافت مستقیم فایل برطرف شود، سیستم را تغییر می‌دهیم تا دوباره فایل‌ها را به صورت مستقیم داخل همین ربات دریافت کنید.*""" - - # ارسال مستقیم متنی از طریق API خام روبیکا - send_payload = { - "chat_id": str(chat_id), - "text": final_caption, - "chat_keypad_type": "New", - "chat_keypad": MAIN_KEYPAD_DICT - } + # --- فاز اول (فوق سریع): استفاده از متدهای اختصاصی rubpy --- + for attempt in range(10): + try: + sent_success = False - for attempt in range(3): + # تلاش برای ارسال عکس + if api_file_type == "Image" and hasattr(client, "send_photo"): try: - send_resp = await session.post( - f"https://botapi.rubika.ir/v3/{rubika_bot_token}/sendMessage", - json=send_payload, - timeout=aiohttp.ClientTimeout(total=15) - ) - s_data = await send_resp.json() + await client.send_photo(chat_id, photo=abs_path, caption=caption) + sent_success = True + except TypeError: + await client.send_photo(chat_id, abs_path, caption=caption) + sent_success = True - if s_data.get("status") == "OK": - return True - else: - error_logs.append(f"Rubika Send API Error: {s_data.get('status')}") - except Exception as e: - error_logs.append(f"Rubika Send HTTP Error: {str(e)[:30]}") - await asyncio.sleep(2) - - # بک‌آپ با کتابخانه در صورت کار نکردن API خام - try: - await client.send_message(chat_id, final_caption) + # تلاش برای ارسال ویس + elif api_file_type == "Voice" and hasattr(client, "send_voice"): + try: + await client.send_voice(chat_id, abs_path, caption=caption) + sent_success = True + except TypeError: + await client.send_voice(chat_id, file=abs_path, caption=caption) + sent_success = True + + # تلاش برای ارسال موزیک + elif api_file_type == "Music" and hasattr(client, "send_music"): + try: + await client.send_music(chat_id, abs_path, caption=caption) + sent_success = True + except TypeError: + await client.send_music(chat_id, music=abs_path, caption=caption) + sent_success = True + + # *** سیستم ضد بمب اتم فاز 1 *** + # اگر هیچکدام از متدهای بالا وجود نداشت یا به هر دلیلی ارسال نشد، حتما به عنوان فایل (سند) بفرست تا معطل نشود + if not sent_success: + if hasattr(client, "send_document"): + try: + await client.send_document(chat_id, document=abs_path, caption=caption) + sent_success = True + except TypeError: + await client.send_document(chat_id, abs_path, caption=caption) + sent_success = True + elif hasattr(client, "send_file"): + try: + await client.send_file(chat_id, file=abs_path) + except TypeError: + await client.send_file(chat_id, abs_path) + if caption: + await send_with_keyboard(client, chat_id, caption, True) + sent_success = True + + if sent_success: return True - except Exception as e: - error_logs.append(f"Rubika Fallback Error: {str(e)[:30]}") - - else: - error_logs.append("خطا در دریافت لینک دانلود از سرور ابری بله.") - - return "❌ خطا در سیستم آپلود ابری:\n" + "\n".join(error_logs[-3:]) - -# ============================================================================== -# 🟢 ۲. تعریف کارگرهای پس‌زمینه عمومی (مشترک برای همه بخش‌ها) -# ============================================================================== -_general_upload_workers = asyncio.Semaphore(20) # ظرفیت 20 عملیات پس‌زمینه همزمان + + except Exception as e: + err_msg = str(e)[:150] + error_logs.append(f"Rubpy Send Error: {err_msg}") + + if "502" in err_msg or "timeout" in err_msg.lower() or "time" in err_msg.lower(): + await asyncio.sleep(4) + else: + await asyncio.sleep(2) -async def background_general_uploader(client, chat_id, file_name, file_type, caption_text, str_chat_id, credit_key=None): - async with _general_upload_workers: + # --- فاز دوم (سنگر آخر): آپلود دستی (Raw HTTP) در صورت مسدودی کامل روب‌پای --- + for attempt in range(15): try: - res = await helper_upload_file(client, chat_id, file_name, file_type, caption_text) - if res is not True: - creds = get_user_credits(str_chat_id) - if not creds.get("is_premium") and credit_key: - user_credits_db[str_chat_id][credit_key] += 1 - save_db(user_credits_db) - await send_with_keyboard(client, chat_id, f"❌ سیستم توزیع ابری گزارش داد:\nمتأسفانه ایجاد لینک دانلود با مشکل مواجه شد و اعتبار شما بازگردانده شد.\n`{str(res)[:500]}`", True) - except: pass - finally: - if os.path.exists(file_name): os.remove(file_name) - + url_request = f"https://botapi.rubika.ir/v3/{bot_token}/requestSendFile" + url_send = f"https://botapi.rubika.ir/v3/{bot_token}/sendFile" + + async with aiohttp.ClientSession() as session: + async with session.post(url_request, json={"type": api_file_type}, timeout=30) as resp: + if resp.status != 200: + error_logs.append(f"Request HTTP {resp.status}") + await asyncio.sleep(2) + continue + + req_data = await resp.json() + if req_data.get("status") == "OK": + upload_url = req_data.get("data", {}).get("upload_url") + if upload_url: + with open(abs_path, "rb") as f: + form = aiohttp.FormData() + form.add_field('file', f, filename=os.path.basename(abs_path)) + async with session.post(upload_url, data=form, timeout=300) as up_resp: + if up_resp.status != 200: + error_logs.append(f"Upload HTTP {up_resp.status}") + if up_resp.status in[502, 503, 500]: + await asyncio.sleep(3) + else: + await asyncio.sleep(1.5) + continue + + try: + up_data = await up_resp.json() + except Exception as json_err: + error_logs.append(f"JSON Decode Error: {json_err}") + await asyncio.sleep(2) + continue + + final_file_id = None + + # *** سیستم ضد بمب اتم فاز 2 *** + # دور زدن ارور روبیکا: اگر روبیکا به فرمت فایل گیر داد، سریعاً نوع را به File تغییر بده تا بدون بررسی فرمت آپلود کند! + if up_data.get("status") == "INVALID_INPUT" and "format" in str(up_data).lower(): + api_file_type = "File" + error_logs.append(f"Format Blocked! Downgrading type to File.") + await asyncio.sleep(1) + continue + + if up_data.get("status") == "OK" or up_data.get("status_det") == "OK": + if "data" in up_data and isinstance(up_data["data"], dict): + final_file_id = up_data["data"].get("file_id") or up_data["data"].get("id") + elif "file_id" in up_data: + final_file_id = up_data.get("file_id") + + if final_file_id: + send_payload = { + "chat_id": str(chat_id), + "file_id": str(final_file_id), + "text": caption, + "chat_keypad_type": "New", + "chat_keypad": MAIN_KEYPAD_DICT + } + async with session.post(url_send, json=send_payload, timeout=30) as send_resp: + if send_resp.status != 200: + error_logs.append(f"Send HTTP {send_resp.status}") + await asyncio.sleep(2) + continue + + s_data = await send_resp.json() + if s_data.get("status") == "OK": return True + else: error_logs.append(f"SendFile API Error: {s_data}") + else: + if up_data.get("status") == "INVALID_INPUT" and api_file_type in ["Voice", "Music"]: + api_file_type = "File" # دور زدن گیر دادن روبیکا + error_logs.append(f"Upload API Error: {up_data}") + else: error_logs.append(f"Missing upload_url in Response: {req_data}") + else: error_logs.append(f"Request API Error: {req_data}") + except Exception as e: + error_logs.append(f"Raw HTTP Error: {str(e)[:100]}") + await asyncio.sleep(2.5) + return "\n".join(error_logs[-6:]) # ============================================================================== # 🟢 پارت 12: توابع تغییر صدا و لیست گویندگان # ============================================================================== -WORKER_URLS = ["https://opera8-ttspro.hf.space/generate"] +# ================================================================== +# لیست‌های اولیه ربات +# ================================================================== +WORKER_URLS =["https://opera8-ttspro.hf.space/generate"] SPEAKERS = { "1": ("شهاب (مرد)", "Charon"), "2": ("آوا (زن)", "Zephyr"), "3": ("نوید (مرد)", "Achird"), @@ -868,100 +791,80 @@ SPEAKERS = { user_states = {} +# ================================================================== +# توابع تغییر صدا و کلون کردن +# ================================================================== async def process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, job_type_name, credit_type): str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip() creds = get_user_credits(str_chat_id) - await send_with_keyboard(client, chat_id, f"⏳ در حال آماده‌سازی فایل‌ها...\n(مدل: {job_type_name})", False) - - job_id = None - total_chunks = 1 - chunks =[] + proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال آماده‌سازی فایل‌ها...\n(مدل: {job_type_name})", False) - # 🛑 پافشاری ۳۰ بار برای آپلود به سرور تغییر صدا (ضد ارور 429) - for attempt in range(30): - async with aiohttp.ClientSession() as session: - # فرم باید در هر تلاش از نو ساخته شود - form = aiohttp.FormData() - form.add_field('source_audio', src_bytes, filename='src.wav', content_type='audio/wav') - form.add_field('ref_audio', ref_bytes, filename='ref.wav', content_type='audio/wav') - try: - async with session.post(f"{VC_BASE_URL}/upload", data=form, timeout=120) as resp: - if resp.status == 429: # شلوغی سرور اسپیس - await asyncio.sleep(15) - continue - if resp.status == 200: - data = await resp.json() - job_id = data.get("job_id") - total_chunks = data.get("total_chunks", 1) - chunks = data.get("chunks",[]) - break - else: - await asyncio.sleep(5) - except Exception: - await asyncio.sleep(5) - - if not job_id: - return await send_with_keyboard(client, chat_id, "❌ پردازش ناموفق بود. ترافیک ربات بسیار بالاست، لطفا چند دقیقه دیگر مجدداً تلاش کنید.", True) - - await send_with_keyboard(client, chat_id, "✅ فایل‌ها ارسال شد. در حال پردازش و تغییر صدا...\n(لطفا چند دقیقه صبور باشید)", False) - - final_filename = None async with aiohttp.ClientSession() as session: - for _ in range(1000): # حلقه بی‌نهایت چک کردن استتوس - await asyncio.sleep(5) + form = aiohttp.FormData() + form.add_field('source_audio', src_bytes, filename='src.wav', content_type='audio/wav') + form.add_field('ref_audio', ref_bytes, filename='ref.wav', content_type='audio/wav') + + try: + async with session.post(f"{VC_BASE_URL}/upload", data=form, timeout=43200) as resp: + if resp.status != 200: + return await send_with_keyboard(client, chat_id, f"❌ خطا در ارسال فایل‌ها (کد {resp.status})", True) + data = await resp.json() + job_id = data.get("job_id") + total_chunks = data.get("total_chunks", 1) + chunks = data.get("chunks",[]) + except Exception as e: + return await send_with_keyboard(client, chat_id, f"❌ خطا در اتصال به سرور:\n{str(e)[:100]}", True) + + await send_with_keyboard(client, chat_id, "✅ فایل‌ها ارسال شد. در حال پردازش و تغییر صدا...\n(لطفا چند دقیقه صبور باشید)", False) + + final_filename = None + for _ in range(10000): + await asyncio.sleep(4) payload_check = {"job_id": job_id, "total_chunks": total_chunks, "chunks": chunks} try: - async with session.post(f"{VC_BASE_URL}/check_status", json=payload_check, timeout=30) as c_resp: + async with session.post(f"{VC_BASE_URL}/check_status", json=payload_check, timeout=20) as c_resp: if c_resp.status == 200: c_data = await c_resp.json() if c_data.get("status") == "completed": final_filename = c_data.get("filename") break elif c_data.get("status") in ["failed", "error"]: - return await send_with_keyboard(client, chat_id, "❌ خطای داخلی سرور در حین ساخت صدا رخ داد.", True) + return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش صدا.", True) except Exception: pass if not final_filename: - return await send_with_keyboard(client, chat_id, "❌ پردازش بیش از حد طول کشید.", True) + return await send_with_keyboard(client, chat_id, "❌ پردازش بیش از حد طول کشید و متوقف شد.", True) download_url = f"{VC_BASE_URL}/download/{final_filename}" await send_with_keyboard(client, chat_id, "📥 پردازش تمام شد! در حال دانلود نتیجه...", False) - - result_bytes = None - for attempt in range(30): - try: - async with session.get(download_url, timeout=120) as d_resp: - if d_resp.status == 200: - result_bytes = await d_resp.read() - break - elif d_resp.status == 429: - await asyncio.sleep(10) - except Exception: - await asyncio.sleep(5) - - if not result_bytes: - return await send_with_keyboard(client, chat_id, "❌ خطا در دریافت فایل نهایی از سرور.", True) + try: + async with session.get(download_url, timeout=43200) as d_resp: + if d_resp.status == 200: + result_bytes = await d_resp.read() + else: + return await send_with_keyboard(client, chat_id, "❌ خطا در دریافت فایل نهایی از سرور.", True) + except Exception: + return await send_with_keyboard(client, chat_id, "❌ خطا در اتصال هنگام دانلود خروجی.", True) file_name_mp3 = f"vc_standard_{uuid.uuid4().hex[:6]}.mp3" await asyncio.to_thread(sync_write_file, file_name_mp3, result_bytes) upload_result = False - # پافشاری بسیار بالا برای آپلود نهایی به روبیکا - for up_att in range(15): + for up_att in range(3): res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", f"🎙️ {job_type_name} شما آماده است!") if res is True: upload_result = True break - await asyncio.sleep(5) + await asyncio.sleep(4) if upload_result is True: if not creds.get("is_premium"): user_credits_db[str_chat_id][credit_type] -= 1 save_db(user_credits_db) else: - await send_with_keyboard(client, chat_id, "❌ فایل با موفقیت پردازش شد اما ارسال آن به دلیل شلوغی سرور روبیکا با مشکل مواجه شد.", True) + await send_with_keyboard(client, chat_id, "❌ فایل پردازش شد اما امکان ارسال در روبیکا فراهم نشد.", True) if os.path.exists(file_name_mp3): os.remove(file_name_mp3) @@ -969,48 +872,37 @@ async def process_legacy_vc_job(client, chat_id, src_bytes, model_url, pitch, mo str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip() creds = get_user_credits(str_chat_id) - await send_with_keyboard(client, chat_id, f"⏳ در حال آماده‌سازی فایل‌ها...\n(مدل: {model_name})", False) + proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال آماده‌سازی فایل‌ها...\n(مدل: {model_name})", False) - job_id = None - # 🛑 پافشاری ۳۰ بار - for attempt in range(30): - async with aiohttp.ClientSession() as session: - form = aiohttp.FormData() - form.add_field('audio_file', src_bytes, filename='input.wav', content_type='audio/wav') - form.add_field('model_url', model_url) - form.add_field('pitch', str(pitch)) - form.add_field('algo', 'rmvpe+') - form.add_field('index_inf', '0.75') - form.add_field('res_filter', '3') - form.add_field('env_ratio', '0.25') - form.add_field('protect', '0.33') - form.add_field('denoise', 'false') - form.add_field('reverb', 'false') - try: - async with session.post(f"{LEGACY_BASE_URL}/upload", data=form, timeout=120) as resp: - if resp.status == 429: - await asyncio.sleep(15) - continue - if resp.status == 200: - data = await resp.json() - job_id = data.get("job_id") - break - else: - await asyncio.sleep(5) - except Exception: - await asyncio.sleep(5) - - if not job_id: - return await send_with_keyboard(client, chat_id, "❌ ترافیک سرور بسیار بالاست. لطفا چند دقیقه دیگر امتحان کنید.", True) - - await send_with_keyboard(client, chat_id, "✅ فایل ارسال شد. در حال پردازش و تغییر صدای شما...\n(لطفا چند دقیقه صبور باشید)", False) - - final_filename = None async with aiohttp.ClientSession() as session: - for _ in range(1000): + form = aiohttp.FormData() + form.add_field('audio_file', src_bytes, filename='input.wav', content_type='audio/wav') + form.add_field('model_url', model_url) + form.add_field('pitch', str(pitch)) + form.add_field('algo', 'rmvpe+') + form.add_field('index_inf', '0.75') + form.add_field('res_filter', '3') + form.add_field('env_ratio', '0.25') + form.add_field('protect', '0.33') + form.add_field('denoise', 'false') + form.add_field('reverb', 'false') + + try: + async with session.post(f"{LEGACY_BASE_URL}/upload", data=form, timeout=43200) as resp: + if resp.status != 200: + return await send_with_keyboard(client, chat_id, f"❌ خطا در ارسال فایل‌ها (کد {resp.status})", True) + data = await resp.json() + job_id = data.get("job_id") + except Exception as e: + return await send_with_keyboard(client, chat_id, f"❌ خطا در اتصال به سرور:\n{str(e)[:100]}", True) + + await send_with_keyboard(client, chat_id, "✅ فایل ارسال شد. در حال پردازش و تغییر صدای شما...\n(لطفا چند دقیقه صبور باشید)", False) + + final_filename = None + for _ in range(10000): await asyncio.sleep(5) try: - async with session.get(f"{LEGACY_BASE_URL}/status/{job_id}", timeout=30) as c_resp: + async with session.get(f"{LEGACY_BASE_URL}/status/{job_id}", timeout=20) as c_resp: if c_resp.status == 200: c_data = await c_resp.json() if c_data.get("status") == "completed": @@ -1026,39 +918,32 @@ async def process_legacy_vc_job(client, chat_id, src_bytes, model_url, pitch, mo download_url = f"{LEGACY_BASE_URL}/download/{final_filename}" await send_with_keyboard(client, chat_id, "📥 پردازش تمام شد! در حال دانلود نتیجه...", False) - - result_bytes = None - for attempt in range(30): - try: - async with session.get(download_url, timeout=120) as d_resp: - if d_resp.status == 200: - result_bytes = await d_resp.read() - break - elif d_resp.status == 429: - await asyncio.sleep(10) - except Exception: - await asyncio.sleep(5) - - if not result_bytes: - return await send_with_keyboard(client, chat_id, "❌ خطا در دریافت فایل نهایی از سرور.", True) + try: + async with session.get(download_url, timeout=43200) as d_resp: + if d_resp.status == 200: + result_bytes = await d_resp.read() + else: + return await send_with_keyboard(client, chat_id, "❌ خطا در دریافت فایل نهایی از سرور.", True) + except Exception: + return await send_with_keyboard(client, chat_id, "❌ خطا در اتصال هنگام دانلود خروجی.", True) file_name_mp3 = f"vc_legacy_{uuid.uuid4().hex[:6]}.mp3" await asyncio.to_thread(sync_write_file, file_name_mp3, result_bytes) upload_result = False - for up_att in range(15): + for up_att in range(3): res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", f"🎙️ صدای خروجی با مدل {model_name} آماده است!") if res is True: upload_result = True break - await asyncio.sleep(5) + await asyncio.sleep(4) if upload_result is True: if not creds.get("is_premium"): user_credits_db[str_chat_id]["voice_conv"] -= 1 save_db(user_credits_db) else: - await send_with_keyboard(client, chat_id, "❌ فایل پردازش شد اما ترافیک روبیکا اجازه آپلود نداد.", True) + await send_with_keyboard(client, chat_id, "❌ فایل پردازش شد اما امکان ارسال در روبیکا فراهم نشد.", True) if os.path.exists(file_name_mp3): os.remove(file_name_mp3) @@ -1229,35 +1114,8 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False) # ============================================================================== -# 🟢 پارت 14: ساخت عکس با هوش مصنوعی (معماری دو قطبی با 3 کارگر اختصاصی ارسال) +# 🟢 پارت 14: ساخت عکس با هوش مصنوعی # ============================================================================== -import asyncio -import os -import uuid - -# 🟢 ساخت 3 کارگر اختصاصی (Worker) فقط برای ارسال فایل -# این 3 کارگر هیچ ربطی به هسته اصلی ربات ندارند و در پس‌زمینه کار می‌کنند -_image_upload_workers = asyncio.Semaphore(3) - -async def background_image_uploader(client, chat_id, file_name, caption_text, str_chat_id): - """این کارگر وظیفه دارد تا هر زمان که طول بکشد، فایل را در پس‌زمینه به روبیکا بفرستد""" - async with _image_upload_workers: - try: - res = await helper_upload_file(client, chat_id, file_name, "Image", caption_text) - if res is not True: - # اگر آپلود به هر دلیلی کلاً شکست خورد، سهمیه کسر شده را به کاربر برمی‌گردانیم - creds = get_user_credits(str_chat_id) - if not creds.get("is_premium"): - user_credits_db[str_chat_id]["image"] += 1 - save_db(user_credits_db) - await send_with_keyboard(client, chat_id, f"❌ سرور ارسال (پس‌زمینه) گزارش داد:\nمتأسفانه روبیکا اجازه آپلود عکس را نداد و سهمیه شما عودت داده شد.\n\n`{str(res)[:800]}`", True) - except Exception as e: - await send_with_keyboard(client, chat_id, f"❌ خطای غیرمنتظره در سرور ارسال:\n{str(e)[:150]}", True) - finally: - # در نهایت فایل فیزیکی را از روی رم/هارد پاک می‌کنیم - if os.path.exists(file_name): - os.remove(file_name) - async def process_image(client, chat_id, prompt, size_choice="1"): str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip() creds = get_user_credits(str_chat_id) @@ -1275,7 +1133,7 @@ async def process_image(client, chat_id, prompt, size_choice="1"): async with aiohttp.ClientSession() as session: for key in keys_to_try_gemini: url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}" - payload = {"contents":[{"parts":[{"text": gemini_sys_prompt}]}], "generationConfig": {"temperature": 0.7}} + payload = {"contents": [{"parts":[{"text": gemini_sys_prompt}]}], "generationConfig": {"temperature": 0.7}} try: async with session.post(url, json=payload, timeout=20) as response: if response.status == 200: @@ -1328,9 +1186,9 @@ async def process_image(client, chat_id, prompt, size_choice="1"): proc_msg = await send_with_keyboard(client, chat_id, f"🎨 در حال طراحی عکس...\n\n📏 **ابعاد:** {size_name}\n📝 **پرامپت ساخته شده:**\n`{short_preview}`\n\n(ممکن است چند ثانیه زمان ببرد)", False) generated_image = None + last_error_log = "هیچ اتصالی برقرار نشد." - # پافشاری ۳۰ بار روی ساخت عکس - for attempt in range(30): + for attempt in range(5): keys_to_try = HF_TOKENS.copy() random.shuffle(keys_to_try) for token in keys_to_try: @@ -1344,12 +1202,10 @@ async def process_image(client, chat_id, prompt, size_choice="1"): ) break except Exception as e: - err_str = str(e).lower() - if "429" in err_str or "too many" in err_str: - await asyncio.sleep(10) + last_error_log = str(e) continue if generated_image: break - await asyncio.sleep(5) + await asyncio.sleep(2) try: if proc_msg: @@ -1359,29 +1215,35 @@ async def process_image(client, chat_id, prompt, size_choice="1"): except Exception: pass if not generated_image: - return await send_with_keyboard(client, chat_id, f"❌ سرورهای ساخت عکس به شدت شلوغ هستند. لطفاً مجدداً امتحان کنید.", True) + return await send_with_keyboard(client, chat_id, f"❌ عکس ساخته نشد.\n\n⚠️ خطا:\n{last_error_log[:200]}", True) try: file_name = f"image_{uuid.uuid4().hex}.jpg" await asyncio.to_thread(sync_save_image, generated_image, file_name) await asyncio.sleep(1) - - # 🟢 پیام آزادسازی ربات به کاربر - await send_with_keyboard(client, chat_id, "✅ تصویر شما با موفقیت ساخته شد!\n\n🚀 فایل جهت ارسال به **تونل اختصاصی آپلود (کارگرهای پس‌زمینه)** تحویل داده شد. ربات اکنون آزاد است و می‌توانید به کار خود ادامه دهید.\n*(تصویر شما به محض عبور از ترافیک روبیکا به صورت خودکار همینجا ارسال خواهد شد)*", False) - caption_text = f"🎨 تصویر شما با پرامپت هوشمند آماده شد!\n\n📏 ابعاد تصویر: {size_name}\n✨ ایده اولیه: {prompt}" - # 🟢 کسر موقت اعتبار (اگر آپلود نشود، در کارگر پس‌زمینه برمی‌گردد) - if not creds.get("is_premium"): - user_credits_db[str_chat_id]["image"] -= 1 - save_db(user_credits_db) - - # 🟢 پرتاب وظیفه به سمت 3 کارگر ارسال (بدون هیچ تایم‌اوتی و بدون درگیری ربات) - asyncio.create_task(background_image_uploader(client, chat_id, file_name, caption_text, str_chat_id)) + upload_result = False + error_log_img = "" + for up_att in range(3): + res = await helper_upload_file(client, chat_id, file_name, "Image", caption_text) + if res is True: + upload_result = True + break + else: + error_log_img = res + await asyncio.sleep(4) + if upload_result is True: + if not creds.get("is_premium"): + user_credits_db[str_chat_id]["image"] -= 1 + save_db(user_credits_db) + else: + await send_with_keyboard(client, chat_id, f"❌ عکس ساخته شد اما آپلود در روبیکا با خطا مواجه شد:\n`{str(error_log_img)[:800]}`", True) + + if os.path.exists(file_name): os.remove(file_name) except Exception as e: - await send_with_keyboard(client, chat_id, f"❌ خطای غیرمنتظره در تولید یا ذخیره عکس رخ داد:\n{str(e)[:150]}\nلطفا دوباره تلاش کنید.", True) - + await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره و ارسال عکس:\n{str(e)[:150]}", True) # ============================================================================== # 🟢 پارت 15: توابع ترجمه و ویرایش عکس با هوش مصنوعی (Flux.2) @@ -1494,7 +1356,7 @@ async def process_image_edit(client, chat_id, image_bytes, prompt): await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس ویرایش شده:\n{str(e)[:150]}", True) # ============================================================================== -# 🟢 پارت 16: ساخت صدا از روی متن (تبدیل متن به صدا - نسخه دوقطبی) +# 🟢 پارت 16: ساخت صدا از روی متن (تبدیل متن به صدا - TTS) # ============================================================================== async def process_tts(client, chat_id, user_text, speaker_id, speaker_name): str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip() @@ -1508,7 +1370,9 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name): headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"} audio_bytes = None - for attempt in range(30): + last_error = "پاسخی دریافت نشد" + + for attempt in range(10): workers = WORKER_URLS.copy() random.shuffle(workers) async with aiohttp.ClientSession(headers=headers, timeout=aiohttp.ClientTimeout(total=600)) as session: @@ -1520,38 +1384,51 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name): if 'audio' in content_type or response.content_length > 1000: audio_bytes = await response.read() break - elif response.status == 429: await asyncio.sleep(10) - except: continue + else: last_error = "فایل نامعتبر" + else: last_error = f"ارور ({response.status})" + except Exception as e: + last_error = f"خطا: {str(e)}" + continue if audio_bytes: break - await asyncio.sleep(5) + await asyncio.sleep(2) try: if proc_msg: msg_id = getattr(proc_msg, 'message_id', None) if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id') if msg_id: await client.delete_messages(chat_id,[msg_id]) - except: pass + except Exception: pass if audio_bytes: file_name_mp3 = f"audio_{uuid.uuid4().hex}.mp3" await asyncio.to_thread(sync_write_file, file_name_mp3, audio_bytes) + await asyncio.sleep(1) - # 🟢 پیام آزادسازی ربات - await send_with_keyboard(client, chat_id, "✅ صدای شما با موفقیت ساخته شد!\n\n🚀 فایل جهت ارسال به **تونل اختصاصی آپلود** تحویل داده شد. ربات اکنون آزاد است.\n*(صدای شما به محض عبور از ترافیک روبیکا خودکار ارسال خواهد شد)*", False) + upload_result_file = False + error_log_tts = "" + for up_att in range(3): + res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", "✅ صدای شما با موفقیت آماده شد (فایل MP3):") + if res is True: + upload_result_file = True + break + else: + error_log_tts = res + await asyncio.sleep(4) - if not creds.get("is_premium"): - user_credits_db[str_chat_id]["tts"] -= 1 - save_db(user_credits_db) - - # ارسال به کارگر پس‌زمینه - asyncio.create_task(background_general_uploader(client, chat_id, file_name_mp3, "Music", "✅ صدای شما آماده است:", str_chat_id, "tts")) + if upload_result_file is True: + if not creds.get("is_premium"): + user_credits_db[str_chat_id]["tts"] -= 1 + save_db(user_credits_db) + else: + await send_with_keyboard(client, chat_id, f"❌ فایل صدا ساخته شد اما سرور روبیکا اجازه آپلود نداد:\n`{str(error_log_tts)[:800]}`", True) + + if os.path.exists(file_name_mp3): os.remove(file_name_mp3) else: - await send_with_keyboard(client, chat_id, "❌ سرورهای تولید صدا شلوغ هستند. بعداً تلاش کنید.", True) - except Exception as e: - await send_with_keyboard(client, chat_id, f"❌ خطای داخلی در تولید صدا:\n{str(e)[:150]}", True) + await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True) + except Exception: traceback.print_exc() # ============================================================================== -# 🟢 پارت 17: سیستم ساخت پادکست (نسخه دوقطبی) +# 🟢 پارت 17: سیستم ساخت پادکست # ============================================================================== async def process_podcast(client, chat_id, prompt): str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip() @@ -1559,50 +1436,72 @@ async def process_podcast(client, chat_id, prompt): if creds["podcast"] <= 0: return await send_with_keyboard(client, chat_id, "❌ اعتبار ساخت پادکست شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False) - proc_msg = await send_with_keyboard(client, chat_id, "📻 در حال نگارش سناریو و ریکورد دیالوگ‌ها...\n(این فرآیند زمان‌بر است، لطفاً صبور باشید)", False) - - available_speakers = [] + proc_msg = await send_with_keyboard(client, chat_id, "📻 در حال بررسی موضوع و نگارش سناریوی پادکست توسط هوش مصنوعی...\n(لطفاً صبور باشید)", False) + available_speakers =[] for num_key, (spk_name, spk_id) in SPEAKERS.items(): gender = "male" if "مرد" in spk_name else "female" available_speakers.append({"id": spk_id, "name": spk_name.split(' (')[0], "gender": gender}) + url_create = "https://opera8-podgen.hf.space/api/create-full-podcast" + payload_create = {"prompt": prompt, "available_speakers": available_speakers} + async with aiohttp.ClientSession() as session: try: - async with session.post("https://opera8-podgen.hf.space/api/create-full-podcast", json={"prompt": prompt, "available_speakers": available_speakers}, timeout=60) as resp: - if resp.status != 202: return await send_with_keyboard(client, chat_id, "❌ خطا در اتصال به سرور پادکست.", True) + async with session.post(url_create, json=payload_create, timeout=60) as resp: + if resp.status != 202: return await send_with_keyboard(client, chat_id, f"❌ خطا در سرور پادکست: وضعیت {resp.status}", True) task_id = (await resp.json()).get("task_id") - except: return await send_with_keyboard(client, chat_id, "❌ سرور پادکست در دسترس نیست.", True) + except Exception as e: return await send_with_keyboard(client, chat_id, f"❌ خطا در اتصال به سرور پادکست:\n{str(e)}", True) + url_status = f"https://opera8-podgen.hf.space/api/podcast-status/{task_id}" script_data = None for _ in range(500): await asyncio.sleep(3) try: - async with session.get(f"https://opera8-podgen.hf.space/api/podcast-status/{task_id}") as resp: + async with session.get(url_status) as resp: if resp.status == 200: status_data = await resp.json() if status_data.get("status") == "completed": script_data = status_data.get("data", {}).get("script",[]) break - elif status_data.get("status") == "failed": break - except: pass + elif status_data.get("status") == "failed": + return await send_with_keyboard(client, chat_id, "❌ متأسفانه هوش مصنوعی نتوانست برای این موضوع سناریو بنویسد.", True) + except Exception: pass - if not script_data: return await send_with_keyboard(client, chat_id, "❌ خطا در تولید سناریو.", True) + if not script_data: return await send_with_keyboard(client, chat_id, "❌ زمان انتظار برای نوشتن سناریو به پایان رسید.", True) + + try: + if proc_msg: + msg_id = getattr(proc_msg, 'message_id', None) + if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id') + if msg_id: await client.delete_messages(chat_id,[msg_id]) + except: pass + + proc_msg = await send_with_keyboard(client, chat_id, f"🎙 سناریو نوشته شد! در حال ریکورد و میکس دیالوگ‌های گویندگان (شامل {len(script_data)} نوبت صحبت)...\nاین مرحله زمان‌بر است...", False) combined_audio = AudioSegment.empty() + url_generate = "https://opera8-podgen.hf.space/api/generate" + for index, turn in enumerate(script_data): + payload_gen = {"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9} chunk_audio_bytes = None + for attempt in range(10): try: - async with session.post("https://opera8-podgen.hf.space/api/generate", json={"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}, timeout=120) as resp: + async with session.post(url_generate, json=payload_gen, timeout=120) as resp: if resp.status == 200: chunk_audio_bytes = await resp.read() break - except: await asyncio.sleep(2) + except Exception: await asyncio.sleep(2) - if chunk_audio_bytes: + if not chunk_audio_bytes: + return await send_with_keyboard(client, chat_id, f"❌ خطا در تولید صدای بخش {index+1} از سرور. عملیات متوقف شد.", True) + + try: combined_audio = await asyncio.to_thread(sync_combine_audio, combined_audio, chunk_audio_bytes) + except Exception as e: + return await send_with_keyboard(client, chat_id, f"❌ خطا در پردازش صدا:\n{str(e)}", True) - file_name_mp3 = f"podcast_{uuid.uuid4().hex}.mp3" + file_name_mp3 = f"final_podcast_{uuid.uuid4().hex}.mp3" await asyncio.to_thread(sync_export_audio, combined_audio, file_name_mp3) try: @@ -1612,14 +1511,27 @@ async def process_podcast(client, chat_id, prompt): if msg_id: await client.delete_messages(chat_id,[msg_id]) except: pass - # 🟢 پیام آزادسازی ربات - await send_with_keyboard(client, chat_id, "✅ پادکست شما با موفقیت ساخته و میکس شد!\n\n🚀 فایل جهت ارسال به **تونل اختصاصی آپلود** تحویل داده شد. ربات اکنون آزاد است.", False) + caption_file = f"🎧 فایل پادکست شما با فرمت MP3:\n\n💡 موضوع شما: {prompt}" - if not creds.get("is_premium"): - user_credits_db[str_chat_id]["podcast"] -= 1 - save_db(user_credits_db) - - asyncio.create_task(background_general_uploader(client, chat_id, file_name_mp3, "Music", f"🎧 پادکست شما آماده است:\n💡 موضوع: {prompt}", str_chat_id, "podcast")) + upload_result_file = False + error_log_pod = "" + for up_att in range(3): + res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", caption_file) + if res is True: + upload_result_file = True + break + else: + error_log_pod = res + await asyncio.sleep(5) + + if upload_result_file is True: + if not creds.get("is_premium"): + user_credits_db[str_chat_id]["podcast"] -= 1 + save_db(user_credits_db) + else: + await send_with_keyboard(client, chat_id, f"❌ پادکست ساخته شد اما روبیکا پس از ده‌ها تلاش خطای آپلود داد.\n\n`{str(error_log_pod)[:800]}`", True) + + if os.path.exists(file_name_mp3): os.remove(file_name_mp3) # ============================================================================== # 🟢 پارت 18: استخراج متن از صدا (STT) و تحلیل انواع فایل با هوش مصنوعی @@ -1769,879 +1681,203 @@ async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt): await send_with_keyboard(client, chat_id, "❌ تمامی سرورها شلوغ هستند. لطفاً بعداً امتحان کنید.", True) # ============================================================================== -# 🟢 پارت 19: ساخت مقاله و خروجی فایل متنی (Word/PDF - نسخه دوقطبی) +# 🟢 پارت 19: ساخت مقاله و خروجی فایل متنی (Word/PDF) # ============================================================================== async def process_create_file(client, chat_id, topic): str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip() creds = get_user_credits(str_chat_id) - if creds["chat"] <= 0: return await send_with_keyboard(client, chat_id, "❌ اعتبار چت شما تمام شده است.", False) + + if creds["chat"] <= 0: + return await send_with_keyboard(client, chat_id, "❌ اعتبار چت شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False) - proc_msg = await send_with_keyboard(client, chat_id, "✍️ در حال تحقیق و نگارش مقاله جامع...\n(لطفاً صبور باشید)", False) + if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلیدهای جیمینای تنظیم نشده‌اند.", False) + + proc_msg = await send_with_keyboard(client, chat_id, "✍️ در حال تحقیق و نگارش مقاله جامع و حرفه‌ای...\n(این عملیات با توجه به طولانی بودن متن ممکن است کمی طول بکشد)", False) - ai_prompt = f"یک مقاله بسیار جامع و حرفه‌ای به زبان فارسی درباره موضوع زیر بنویس:\nموضوع: {topic}" + ai_prompt = f"یک مقاله بسیار جامع، کاملا حرفه‌ای، بسیار طولانی و با جزئیات کامل درباره موضوع زیر به زبان فارسی بنویس. فقط متن مقاله را بده و هیچ توضیح اضافه‌ای نده:\n\nموضوع: {topic}" + article_text = None - # ... بخش تولید متن مقاله توسط Gemini یا HF (همان کدهای قبلی شما) ... - # [برای اختصار فرض بر تولید article_text است] + for attempt in range(5): + keys_to_try = get_next_gemini_keys(100) + async with aiohttp.ClientSession() as session: + for key in keys_to_try: + url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}" + payload = {"contents":[{"parts":[{"text": ai_prompt}]}], "generationConfig": {"temperature": 0.7, "maxOutputTokens": 8192}} + try: + async with session.post(url, json=payload, timeout=60) as response: + if response.status == 200: + data = await response.json() + article_text = data["candidates"][0]["content"]["parts"][0]["text"] + break + except Exception: continue + if article_text: break + await asyncio.sleep(2) + + if not article_text and HF_TOKENS: + for attempt in range(5): + keys_to_try_hf = HF_TOKENS.copy() + random.shuffle(keys_to_try_hf) + async with aiohttp.ClientSession() as session: + for hf_key in keys_to_try_hf: + url = "https://router.huggingface.co/v1/chat/completions" + headers = {"Authorization": f"Bearer {hf_key}", "Content-Type": "application/json"} + payload = { + "model": "google/gemma-4-31B-it:novita", + "messages":[{"role": "user", "content":[{"type": "text", "text": ai_prompt}]}], + "max_tokens": 4096 + } + try: + async with session.post(url, headers=headers, json=payload, timeout=60) as response: + if response.status == 200: + data = await response.json() + article_text = data["choices"][0]["message"]["content"] + break + except Exception: continue + if article_text: break + await asyncio.sleep(2) - if not article_text: return await send_with_keyboard(client, chat_id, "❌ خطا در تولید متن مقاله.", True) + if not article_text: + return await send_with_keyboard(client, chat_id, "❌ سرور تولید متن شلوغ است، لطفاً بعداً تلاش کنید.", True) try: if proc_msg: msg_id = getattr(proc_msg, 'message_id', None) if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id') if msg_id: await client.delete_messages(chat_id,[msg_id]) - except: pass + except Exception: pass - # 🟢 پیام آزادسازی ربات - await send_with_keyboard(client, chat_id, "✅ نگارش مقاله به پایان رسید!\n\n🚀 فایل‌های PDF و Word جهت تبدیل و ارسال به **تونل اختصاصی** تحویل داده شدند. ربات آزاد است.", False) + proc_msg = await send_with_keyboard(client, chat_id, "📄 مقاله با موفقیت نوشته شد! در حال ارتباط با سرور و تبدیل به فایل‌های PDF و Word...\n(لطفا صبور باشید)", False) - if not creds.get("is_premium"): - user_credits_db[str_chat_id]["chat"] -= 1 - save_db(user_credits_db) + converter_url = "https://opera8-texttopdf.hf.space/" + uid = uuid.uuid4().hex - # کارگر اختصاصی برای تبدیل و آپلود مقاله (برای اینکه ربات معطل تبدیل PDF هم نشود) - async def article_worker(): - uid = uuid.uuid4().hex - converter_url = "https://opera8-texttopdf.hf.space/" - async with aiohttp.ClientSession() as session: - # تلاش برای PDF - try: - form = aiohttp.FormData() - form.add_field('content', article_text); form.add_field('format', 'pdf') - async with session.post(converter_url, data=form, timeout=90) as resp: + pdf_success = False + for attempt in range(30): + try: + pdf_bytes = None + async with aiohttp.ClientSession() as session: + form_data = aiohttp.FormData() + form_data.add_field('content', article_text) + form_data.add_field('format', 'pdf') + async with session.post(converter_url, data=form_data, timeout=90) as resp: if resp.status == 200: - fname = f"Art_{uid}.pdf" - with open(fname, "wb") as f: f.write(await resp.read()) - await background_general_uploader(client, chat_id, fname, "File", f"📄 PDF مقاله: {topic}", str_chat_id) - except: pass - # تلاش برای Word - try: - form = aiohttp.FormData() - form.add_field('content', article_text); form.add_field('format', 'docx') - async with session.post(converter_url, data=form, timeout=90) as resp: + data_bytes = await resp.read() + if data_bytes and len(data_bytes) > 100: + pdf_bytes = data_bytes + + if pdf_bytes: + filename = f"Article_{uid}.pdf" + await asyncio.to_thread(sync_write_file, filename, pdf_bytes) + + res_upload = False + for _ in range(3): + r = await helper_upload_file(client, chat_id, filename, "File", f"📄 فایل PDF مقاله شما:\n\n💡 موضوع: {topic}") + if r is True: + res_upload = True + break + await asyncio.sleep(4) + + if os.path.exists(filename): + os.remove(filename) + + if res_upload is True: + pdf_success = True + break + except Exception as e: + print(f"PDF error (attempt {attempt+1}):", e) + + await asyncio.sleep(4) + + await asyncio.sleep(3.5) + + docx_success = False + for attempt in range(30): + try: + docx_bytes = None + async with aiohttp.ClientSession() as session: + form_data = aiohttp.FormData() + form_data.add_field('content', article_text) + form_data.add_field('format', 'docx') + async with session.post(converter_url, data=form_data, timeout=90) as resp: if resp.status == 200: - fname = f"Art_{uid}.docx" - with open(fname, "wb") as f: f.write(await resp.read()) - await background_general_uploader(client, chat_id, fname, "File", f"📝 Word مقاله: {topic}", str_chat_id) - except: pass + data_bytes = await resp.read() + if data_bytes and len(data_bytes) > 100: + docx_bytes = data_bytes + + if docx_bytes: + filename = f"Article_{uid}.docx" + await asyncio.to_thread(sync_write_file, filename, docx_bytes) + + res_upload = False + for _ in range(3): + r = await helper_upload_file(client, chat_id, filename, "File", f"📝 فایل Word (DOCX) مقاله شما:\n\n💡 موضوع: {topic}") + if r is True: + res_upload = True + break + await asyncio.sleep(4) + + if os.path.exists(filename): + os.remove(filename) + + if res_upload is True: + docx_success = True + break + except Exception as e: + print(f"DOCX error (attempt {attempt+1}):", e) + + await asyncio.sleep(4) - asyncio.create_task(article_worker()) + try: + if proc_msg: + msg_id = getattr(proc_msg, 'message_id', None) + if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id') + if msg_id: await client.delete_messages(chat_id,[msg_id]) + except Exception: pass + if pdf_success and docx_success: + if not creds.get("is_premium"): + user_credits_db[str_chat_id]["chat"] -= 1 + save_db(user_credits_db) + await send_with_keyboard(client, chat_id, "✅ مقاله شما با موفقیت به صورت هر دو فایل (PDF و Word) تحویل داده شد!", True) + elif pdf_success or docx_success: + if not creds.get("is_premium"): + user_credits_db[str_chat_id]["chat"] -= 1 + save_db(user_credits_db) + await send_with_keyboard(client, chat_id, "⚠️ یکی از فایل‌ها با موفقیت ارسال شد اما دیگری پس از تلاش‌های مکرر سرور با خطا مواجه شد.", True) + else: + await send_with_keyboard(client, chat_id, "❌ متأسفانه پس از تلاش‌های مکرر، سرور قادر به ساخت و ارسال هیچ‌یک از فایل‌ها نبود و اعتباری از شما کسر نشد.", True) # ============================================================================== -# 🟢 پارت 20: بدنه اصلی ربات (Handler)، دستورات مدیریتی، حلقه اصلی و استارت +# 🔴 پارت 20: نسخه پاکسازی اضطراری (مخصوص تخلیه صف در دیتابیس جدید v3) # ============================================================================== -# ================================================================== -# حلقه و مدیریت اصلی ربات -# ================================================================== - -# 🧹 سیستم پاکسازی هوشمند رم (Garbage Collector) مخصوص هزاران کاربر -async def ram_garbage_collector(): - """این تابع در پس‌زمینه می‌چرخد و فایل‌های رها شده در حافظه را پاک می‌کند تا رم سرور پر نشود""" - while True: - await asyncio.sleep(1800) # هر ۳۰ دقیقه یکبار بررسی می‌کند - now = time.time() - try: - for uid in list(user_states.keys()): - # اگر کاربر بیش از ۱ ساعت (۳۶۰۰ ثانیه) با ربات کار نکرده بود - if now - user_states[uid].get("last_time", 0) > 3600: - # پاک کردن بایت‌های فایل‌های سنگین از رم سرور - if user_states[uid].get("file_bytes") is not None: - user_states[uid]["file_bytes"] = None - if user_states[uid].get("ref_bytes") is not None: - user_states[uid]["ref_bytes"] = None - # خالی کردن تاریخچه چت برای آزادسازی رم (نگه داشتن ۵ پیام آخر) - if len(user_states[uid].get("history",[])) > 5: - user_states[uid]["history"] = user_states[uid]["history"][-5:] - except Exception as e: - pass if not bot_token: - print("خطا: توکن ربات روبیکا وارد نشده است!") + print("خطا: توکن وارد نشده!") else: bot = BotClient(bot_token) - # دکوریتور @bot.on_update حذف شد چون با وب‌هوک خودمان مستقیم این تابع را صدا می‌زنیم + @bot.on_update(filters.private) async def main_handler(client, update): - global BOT_GUID - try: - if not BOT_GUID: - try: - me_info = await client.get_me() - if me_info and hasattr(me_info, 'user'): - BOT_GUID = getattr(me_info.user, 'user_guid', None) - except Exception: - pass - + # فقط استخراج آیدی پیام msg_obj = getattr(update, "message", None) or getattr(update, "new_message", None) - - author_id = getattr(update, 'author_guid', None) - if not author_id and msg_obj: - author_id = msg_obj.get('author_object_guid') if isinstance(msg_obj, dict) else getattr(msg_obj, 'author_object_guid', None) - - if BOT_GUID and author_id == BOT_GUID: - return - - chat_id = getattr(update, 'object_guid', None) or getattr(update, 'author_guid', None) or getattr(update, "chat_id", None) - if not chat_id: return - msg_id = getattr(update, "message_id", None) if not msg_id and msg_obj: msg_id = msg_obj.get("message_id") if isinstance(msg_obj, dict) else getattr(msg_obj, "message_id", None) - str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip() - creds = get_user_credits(str_chat_id) - - # =================================================================== - # 🛑 سیستم صد در صد قطعی ضد تکرار پیام با استفاده از دیتابیس ابری - # =================================================================== - if msg_id: - str_msg_id = str(msg_id).strip() - # 1. اگر پیام قبلاً در دیتابیس ثبت شده بود، ربات بی‌درنگ کار را متوقف می‌کند - if is_message_processed(str_msg_id): - return - # 2. اگر پیام جدید بود، در همان لحظه در دیتابیس ذخیره می‌شود تا دیگر هرگز خوانده نشود - mark_message_processed(str_msg_id) - # =================================================================== - - user_text = getattr(update, "text", "") or getattr(msg_obj, "text", "") - user_text_str = str(user_text).strip() if user_text else "" - user_text_lower = user_text_str.lower() - - if is_user_spamming(str_chat_id): - return + if not msg_id: return + str_msg_id = str(msg_id).strip() - # =================================================================== - # 🟢 ثبت آخرین زمان فعالیت کاربر برای سیستم پاکسازی رم - # =================================================================== - if str_chat_id not in user_states: - user_states[str_chat_id] = {"mode": None, "text": "", "history":[], "file_bytes": None, "file_name": None, "last_time": time.time()} - else: - user_states[str_chat_id]["last_time"] = time.time() # آپدیت زمان فعالیت - - if user_text_lower.startswith(f"{ADMIN_CODE} pro=") or user_text_lower.startswith(f"{ADMIN_CODE}pro="): - parts = user_text_str.split("=", 1) - if len(parts) >= 2: - target_id = parts[1].replace("`", "").replace("'", "").replace('"', "").strip() - if target_id: - if target_id not in user_credits_db or not isinstance(user_credits_db[target_id], dict): - user_credits_db[target_id] = { - "is_premium": False, - "expire_date": None, - "last_reset": "", - "chat": 10, - "image": 5, - "edit_image": 1, - "podcast": 2, - "tts": 5, - "file": 1, - "stt": 5, - "voice_conv": 3, - "voice_clone": 1, - "last_msg_id": 0, - "has_joined": True, - "invited_count": 0, - "used_referral": False, - "referral_code": "" - } - - user_credits_db[target_id]["is_premium"] = True - expire_time = datetime.datetime.now() + datetime.timedelta(days=30) - user_credits_db[target_id]["expire_date"] = expire_time.isoformat() - - user_credits_db[target_id]["chat"] = 999999 - user_credits_db[target_id]["image"] = 20 - user_credits_db[target_id]["edit_image"] = 10 - user_credits_db[target_id]["podcast"] = 999999 - user_credits_db[target_id]["tts"] = 999999 - user_credits_db[target_id]["file"] = 999999 - user_credits_db[target_id]["stt"] = 999999 - user_credits_db[target_id]["voice_conv"] = 999999 - user_credits_db[target_id]["voice_clone"] = 999999 - - save_db(user_credits_db) - await send_with_keyboard(client, chat_id, f"✅ حساب کاربر `{target_id}` به مدت ۳۰ روز شارژ شد و به پرو ارتقا یافت.", False) - try: - await send_with_keyboard(client, target_id, "🎉 **کاربر گرامی، تبریک!**\n\nحساب شما با موفقیت توسط پشتیبانی به **🌟 نسخه پرو (ویژه)** ارتقا یافت.\nهم‌اکنون بسته‌های نامحدود و طلایی یک‌ماهه شما فعال گردید.\n\nجهت مشاهده جزئیات روی دکمه «حساب کاربری 👤» کلیک کنید.", True) - except Exception: pass - return - - if user_text_lower.startswith(f"{ADMIN_CODE} free=") or user_text_lower.startswith(f"{ADMIN_CODE}free="): - parts = user_text_str.split("=", 1) - if len(parts) >= 2: - target_id = parts[1].replace("`", "").replace("'", "").replace('"', "").strip() - if target_id: - if target_id in user_credits_db and isinstance(user_credits_db[target_id], dict): - user_credits_db[target_id]["is_premium"] = False - user_credits_db[target_id]["expire_date"] = None - user_credits_db[target_id]["last_reset"] = "" - save_db(user_credits_db) - await send_with_keyboard(client, chat_id, f"✅ اشتراک کاربر `{target_id}` لغو شد و به رایگان تبدیل گشت.", False) - try: - await send_with_keyboard(client, target_id, "⚠️ کاربر گرامی، اشتراک ویژه شما به پایان رسید و حساب شما به نسخه رایگان (آزمایشی) تغییر یافت.", True) - except Exception: pass - return - - if user_text_lower.startswith(f"{ADMIN_CODE} =") or user_text_lower.startswith(f"{ADMIN_CODE}="): - parts = user_text_str.split("=", 1) - if len(parts) >= 2: - target_id = parts[1].replace("`", "").replace("'", "").replace('"', "").strip() - if target_id: - if target_id in user_credits_db and isinstance(user_credits_db[target_id], dict): - t_creds = user_credits_db[target_id] - is_prem = t_creds.get("is_premium", False) - - if is_prem: - expire_txt = "نامشخص" - days_left = 0 - if t_creds.get("expire_date"): - exp_d = datetime.datetime.fromisoformat(t_creds["expire_date"]) - now = datetime.datetime.now() - diff = exp_d - now - days_left = diff.days if diff.days >= 0 else 0 - - jy, jm, jd = gregorian_to_jalali(exp_d.year, exp_d.month, exp_d.day) - expire_txt = f"{jy:04d}/{jm:02d}/{jd:02d}" - status_text = f"🌟 نسخه پرو (ویژه)\n📅 انقضا: {expire_txt}\n⏳ باقیمانده: {days_left} روز" - else: - status_text = "🥉 نسخه رایگان (آزمایشی)\n⏳ وضعیت: سهمیه روزانه محدود" - - chat_rem = "نامحدود ∞" if is_prem else t_creds.get('chat', 0) - vc_rem = "نامحدود ∞" if is_prem else t_creds.get('voice_conv', 0) - clone_rem = "نامحدود ∞" if is_prem else t_creds.get('voice_clone', 0) - podcast_rem = "نامحدود ∞" if is_prem else t_creds.get('podcast', 0) - tts_rem = "نامحدود ∞" if is_prem else t_creds.get('tts', 0) - file_rem = "نامحدود ∞" if is_prem else t_creds.get('file', 0) - stt_rem = "نامحدود ∞" if is_prem else t_creds.get('stt', 0) - image_rem = t_creds.get('image', 0) - edit_image_rem = t_creds.get('edit_image', 0) - invs = t_creds.get('invited_count', 0) - joined_status = "بله ✅" if t_creds.get('has_joined') else "خیر ❌" - - info_msg = f"""🔍 **اطلاعات اختصاصی کاربر:** -👤 شناسه: `{target_id}` -عضو کانال: {joined_status} - -🔹 **وضعیت اشتراک:** -{status_text} -🎁 **تعداد افراد دعوت شده:** {invs} نفر - -📊 **سهمیه باقی‌مانده فعلی:** -💬 چت: {chat_rem} -🎨 تولید عکس: {image_rem} -🪄 ویرایش عکس: {edit_image_rem} -🎙️ تغییر صدا: {vc_rem} -👤 کلون کردن صدا: {clone_rem} -🎙 پادکست: {podcast_rem} -🗣 متن به صدا: {tts_rem} -📁 تحلیل فایل: {file_rem} -📝 صدا به متن: {stt_rem}""" - await send_with_keyboard(client, chat_id, info_msg, False) - else: - await send_with_keyboard(client, chat_id, f"❌ کاربری با شناسه `{target_id}` در دیتابیس ربات یافت نشد.", False) - return - - is_file = False - file_name = f"unknown_file_{uuid.uuid4().hex[:6]}.jpg" - - if msg_obj: - for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']: - file_attr = getattr(msg_obj, attr, None) - if file_attr: - is_file = True - file_name = getattr(file_attr, 'file_name', file_name) - break - - if not is_file and hasattr(msg_obj, 'to_dict'): - msg_dict = msg_obj.to_dict() - for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']: - if attr in msg_dict: - is_file = True - file_attr = msg_dict.get(attr, {}) - file_name = file_attr.get('file_name', file_name) if isinstance(file_attr, dict) else file_name - break - - if user_text_str == "✅ عضو شدم": - if not creds.get("has_joined", False): - user_credits_db[str_chat_id]["has_joined"] = True - save_db(user_credits_db) - await send_with_keyboard(client, chat_id, "🎉 **عضویت شما با موفقیت تایید شد! خیلی خوش آمدید.**\n\nحالا می‌توانید از تمامی امکانات ربات استفاده کنید.\nلطفاً یکی از گزینه‌های منو را انتخاب کنید:", True) - return - else: - await send_with_keyboard(client, chat_id, "✅ شما قبلاً عضو شده‌اید! لطفا از منوی اصلی استفاده کنید.", True) - return - - if not creds.get("has_joined", False): - join_msg = "👋 **سلام کاربر گرامی!**\n\nجهت استفاده از خدمات ربات هوش مصنوعی آلفا و مطلع شدن از آخرین آپدیت‌ها، لطفا ابتدا در کانال رسمی ما عضو شوید:\n\n📢 **آیدی کانال:** @aialpha\n\n👇 پس از عضویت در کانال، بر روی دکمه **«✅ عضو شدم»** کلیک کنید تا ربات برای شما فعال شود." - try: - payload = {"chat_id": chat_id, "text": join_msg, "chat_keypad_type": "New", "chat_keypad": JOIN_KEYPAD_DICT} - await client._make_request("sendMessage", payload) - except Exception: - await client.send_message(chat_id, join_msg) - return - - if user_text_lower.startswith("/start"): - user_states[str_chat_id]["mode"] = None - user_states[str_chat_id]["file_bytes"] = None - await send_with_keyboard(client, chat_id, "سلام! به ربات هوش مصنوعی آلفا خوش آمدید 🤖\n\nلطفاً برای شروع، از کیبورد پایین یکی از بخش‌ها را انتخاب کنید:", True) - return - - if user_text_str in["سلام", "لغو", "/cancel", "❌ لغو", "برگشت♻️"]: - user_states[str_chat_id]["mode"] = None - user_states[str_chat_id]["file_bytes"] = None - await send_with_keyboard(client, chat_id, "سلام! به ربات هوش مصنوعی آلفا خوش آمدید 🤖\n\nلطفاً برای شروع، از کیبورد پایین یکی از بخش‌ها را انتخاب کنید:", True) - return - - if user_text_str in["/account", "حساب کاربری 👤"]: - is_prem = creds.get("is_premium", False) - - if is_prem: - expire_txt = "نامشخص" - days_left = 0 - if creds.get("expire_date"): - exp_d = datetime.datetime.fromisoformat(creds["expire_date"]) - now = datetime.datetime.now() - diff = exp_d - now - days_left = diff.days if diff.days >= 0 else 0 - - jy, jm, jd = gregorian_to_jalali(exp_d.year, exp_d.month, exp_d.day) - expire_txt = f"{jy:04d}/{jm:02d}/{jd:02d} (ساعت {exp_d.hour:02d}:{exp_d.minute:02d})" - - status_text = "🌟 نسخه پرو (ویژه)" - expire_info = f"\n📅 **تاریخ انقضا:** {expire_txt}\n⏳ **زمان باقیمانده:** {days_left} روز" - daily_note = "*نکته: سهمیه پردازشی شما مختص همین دوره یک‌ماهه می‌باشد.*" - else: - status_text = "🥉 نسخه رایگان (آزمایشی)" - expire_info = "" - daily_note = "*نکته: سهمیه شما هر روز ساعت ۰۰:۰۰ بامداد به صورت خودکار مجدداً شارژ می‌گردد.*" - - chat_rem = "نامحدود ∞" if is_prem else creds['chat'] - vc_rem = "نامحدود ∞" if is_prem else creds.get('voice_conv', 0) - clone_rem = "نامحدود ∞" if is_prem else creds.get('voice_clone', 0) - podcast_rem = "نامحدود ∞" if is_prem else creds['podcast'] - tts_rem = "نامحدود ∞" if is_prem else creds['tts'] - file_rem = "نامحدود ∞" if is_prem else creds['file'] - stt_rem = "نامحدود ∞" if is_prem else creds['stt'] - image_rem = creds['image'] - edit_image_rem = creds['edit_image'] - invited_count = creds.get('invited_count', 0) - my_code = get_or_create_referral_code(str_chat_id) - - account_profile = f"""👤 **اطلاعات حساب کاربری شما** - -🔹 **شناسه یکتا:** `{chat_id}` -🎫 **کد هدیه شما:** `{my_code}` -🔹 **وضعیت اشتراک:** {status_text}{expire_info} -🎁 **تعداد افراد دعوت شده:** {invited_count} نفر - -📊 **سهمیه باقی‌مانده شما:** -- 💬 چت هوشمند: {chat_rem} -- 🎨 تولید تصویر: {image_rem} عدد -- 🪄 ویرایش تصویر: {edit_image_rem} عدد -- 🎙️ تغییر صدا: {vc_rem} -- 👤 کلون کردن صدا: {clone_rem} -- 🎙 ساخت پادکست: {podcast_rem} -- 🗣 تبدیل متن به صدا: {tts_rem} -- 📁 تحلیل فایل و سند: {file_rem} -- 📝 تبدیل صدا به متن: {stt_rem} - -{daily_note}""" - await send_with_keyboard(client, chat_id, account_profile, True) - return - - if user_text_str in["/invite", "دعوت دوستان 🎁"]: - invites = creds.get("invited_count", 0) - remains = 10 - (invites % 10) - my_code = get_or_create_referral_code(str_chat_id) - - invite_text = f"""🎁 **سیستم دعوت دوستان (دو سر سود)** - -با دعوت دوستان خود به ربات آلفا، هم شما و هم دوستتان هدیه می‌گیرید! -به دوست خود بگویید پس از ورود به ربات، دکمه **«ثبت کد هدیه 🎫»** را بزند و کد زیر را وارد کند. -✨ **سود دوست شما:** در همان لحظه 10 تبدیل رایگان متن به صدا دریافت می‌کند. -✨ **سود شما:** به آمار دعوت‌هایتان اضافه می‌شود و به ازای هر **10 نفر**، **3 روز اشتراک پرو نامحدود** می‌گیرید. - -📊 **آمار شما:** -- تعداد دعوت‌های موفق: {invites} نفر -- دعوت‌های باقی‌مانده تا جایزه بعدی: {remains} نفر - -کد هدیه اختصاصی شما: -`{my_code}` - -(متن زیر را کپی کرده و برای دوستانتان بفرستید 👇)""" - await send_with_keyboard(client, chat_id, invite_text, True) - - forward_text = f"""🤖 **ربات هوش مصنوعی آلفا پرو** - -✨ با این ربات می‌تونی کارهای زیر رو به راحتی انجام بدی: -💬 چت با پیشرفته‌ترین هوش مصنوعی -🎨 ساخت و ویرایش حرفه‌ای عکس -🎙️ تغییر صدا و کلون کردن صدا -🎙 ساخت پادکست اختصاصی -🗣 تبدیل متن به صدا (30 گوینده مختلف) -📝 تبدیل صدا و ویدیو به متن -📁 تحلیل فایل‌ها و ساخت مقاله - -👇 اول وارد ربات زیر شو: -@aialphabot - -سپس دکمه **«ثبت کد هدیه 🎫»** را بزن و کد 8 رقمی زیر رو وارد کن تا همون اول **10 تا تبدیل صدا هدیه بگیری**: -`{my_code}`""" - await send_with_keyboard(client, chat_id, forward_text, False) - return - - if user_text_str in["/referral", "ثبت کد هدیه 🎫"]: - if creds.get("used_referral", False): - await send_with_keyboard(client, chat_id, "❌ شما قبلاً کد هدیه یک نفر را ثبت کرده‌اید و فقط یک‌بار مجاز به استفاده از این امکان هستید.", True) - return - user_states[str_chat_id]["mode"] = "waiting_for_referral_code" - msg = "🎫 **ثبت کد هدیه**\n\nکد هدیه 8 رقمی (اعداد) که از دوست خود دریافت کرده‌اید را اینجا وارد کنید تا در همان لحظه **10 سهمیه تبدیل رایگان متن به صدا** هدیه بگیرید!\n\n(برای انصراف دکمه «برگشت♻️» را بزنید)" - await send_with_keyboard(client, chat_id, msg, True) - return - - if user_text_str in["/buy", "خرید اشتراک 💎"]: - buy_text = f"""💎 **خرید اشتراک ویژه آلفا پرو (یک ماهه)** - -با تهیه اشتراک ویژه، محدودیت‌ها را کنار بزنید و از نهایت قدرت هوش مصنوعی لذت ببرید! 🚀 - -🎁 **بسته طلایی یک‌ماهه شامل:** -🤖 چت با هوش مصنوعی بصورت نامحدود -🎙️ تغییر صدا و کلون کردن صدای نامحدود -🗣 تبدیل متن به صدا بصورت نامحدود با ۳۰ گوینده -🎙 ساخت پادکست بصورت نامحدود - -🪄 ۱۰ ویرایش تصویر -🎨 ۲۰ تولید تصویر -📁 تحلیل نامحدود فایل و سند -📝 تبدیل فایل صوتی به متن نامحدود - -💳 **هزینه اشتراک یک ماهه:** 250 هزار تومان - -💳 **شماره کارت جهت واریز:** -`6219861411958035` -👤 **به نام:** کوهی - -✅ **نحوه فعال‌سازی:** -پس از واریز مبلغ، لطفاً رسید پرداختی را به همراه **شناسه یکتای خود** (که در پایین آمده) به آیدی پشتیبانی زیر ارسال کنید تا اشتراک شما فعال گردد: - -🔑 **شناسه یکتای شما:** `{chat_id}` - -👨‍💻 **ارتباط با پشتیبانی:** -🆔 @H_a_m_e_d100""" - await send_with_keyboard(client, chat_id, buy_text, True) - return - - if user_text_str in["/transfer", "انتقال اکانت از برنامه به ربات"]: - transfer_text = f"""🔄 **انتقال اکانت از برنامه به ربات** - -کاربر گرامی، در صورتی که داخل برنامه «هوش مصنوعی آلفا» پیش‌تر اشتراک تهیه کرده‌اید، نیازی به خرید مجدد اشتراک داخل ربات نیست! 🎉 - -کافیست **شناسه یکتای** ربات روبیکای خود را کپی کرده و برای پشتیبانی ما در برنامه هوش مصنوعی آلفا ارسال کنید تا اکانت اشتراکی شما به سرعت از برنامه به ربات روبیکا انتقال داده شود. - -🔑 **شناسه یکتای ربات شما:** `{chat_id}` - -👨‍💻 **دقت کنید شناسه ربات رو به پشتیبانی داخل خود برنامه هوش مصنوعی آلفا ارسال کنید:** -🆔 @H_a_m_e_d100""" - await send_with_keyboard(client, chat_id, transfer_text, True) - return - - if user_text_str in["/chat", "💬 چت", "چت با هوش مصنوعی 🤖"]: - user_states[str_chat_id]["mode"] = "chat" - user_states[str_chat_id]["history"] =[] - await send_with_keyboard(client, chat_id, "💬 شما وارد بخش **چت با هوش مصنوعی** شدید.\n\nهر سوالی دارید بفرستید تا جواب بدم:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True) - return - - if user_text_str in["/image", "🎨 عکس", "ساخت تصاویر🎨"]: - user_states[str_chat_id]["mode"] = "image_waiting_for_text" - await send_with_keyboard(client, chat_id, "🎨 شما وارد بخش **ساخت عکس پیشرفته** شدید.\n\nمتن (ایده) خود را به صورت کامل ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True) - return - - if user_text_str in["/edit_image", "ویرایش تصاویر 🪄"]: - user_states[str_chat_id]["mode"] = "image_edit_waiting_for_image" - user_states[str_chat_id]["file_bytes"] = None - await send_with_keyboard(client, chat_id, "🪄 به بخش **ویرایش عکس (Flux.2)** خوش آمدید.\n\nلطفاً ابتدا عکسی که می‌خواهید ویرایش کنید را بفرستید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True) - return - - if user_text_str in["/tts", "🎙️ صدا", "تبدیل متن به صدا🗣️"]: - user_states[str_chat_id]["mode"] = "tts_waiting_for_text" - await send_with_keyboard(client, chat_id, "🎙️ شما وارد بخش **تبدیل متن به صدا** شدید.\n\nلطفاً متنی که می‌خواهید به صدا تبدیل شود را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True) - return + # ثبت در دیتابیس v3 بدون فرستادن هیچ پاسخی + if not is_message_processed(str_msg_id): + mark_message_processed(str_msg_id) + print(f"🗑️ [V3-CLEARING] پیام {str_msg_id} ثبت شد.") - if user_text_str in["/podcast", "📻 پادکست", "ساخت پادکست 🎙️"]: - user_states[str_chat_id]["mode"] = "podcast_waiting_for_topic" - await send_with_keyboard(client, chat_id, "📻 شما وارد بخش **ساخت پادکست** شدید.\n\nلطفاً موضوع پادکست خود را بفرستید.\nمثال: درباره تاریخچه پیدایش قهوه با ۳ گوینده یک پادکست جذاب بساز . همچنین این قسمت متصل به مدل زبانی است و درخواست هارو قبل از ساخت درک میکنه. میتوانید مقاله کامل یک سایت بفرستید با تبلیغات یا هرچی، هوش مصنوعی متن مقاله رو استخراج و پادکست براتون میسازه . در توضیحات امکان مشخص کردن تعداد گوینده به همراه اسم شون نیز از سمت شما امکان پذیر است.\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True) - return - - # =============================================== - # بخش‌های تغییر صدا و کلون کردن صدا - # =============================================== - if user_text_str in["/vc", "تغییر صدا 🎙️"]: - user_states[str_chat_id]["mode"] = "vc_waiting_for_voice" - user_states[str_chat_id]["file_bytes"] = None - await send_with_keyboard(client, chat_id, "🎙️ شما وارد بخش **تغییر صدا** شدید.\n\nلطفاً صدای خود (ویس) را بفرستید تا آن را به صدای شخصیت‌های معروف تبدیل کنم:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True) - return - - if user_text_str in["/clone", "کلون کردن صدا ��"]: - user_states[str_chat_id]["mode"] = "clone_waiting_for_src" - user_states[str_chat_id]["file_bytes"] = None - user_states[str_chat_id]["ref_bytes"] = None - await send_with_keyboard(client, chat_id, "👤 شما وارد بخش **کلون کردن صدای اختصاصی** شدید.\n\nدر این بخش شما به 2 فایل صوتی نیاز دارید:\n1️⃣ صدای خودتان (ورودی)\n2️⃣ صدای شخص هدف (الگو)\n\nابتدا **صدای خودتان (ورودی)** را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True) - return - # =============================================== - - if user_text_str in["/file", "تحلیل فایل 📁"]: - user_states[str_chat_id]["mode"] = "file_waiting_for_file" - user_states[str_chat_id]["file_bytes"] = None - await send_with_keyboard(client, chat_id, "📁 شما وارد بخش **تحلیل فایل اختصاصی** شدید.\n\nلطفاً فایل خود را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True) - return - - if user_text_str in["/stt", "فایل صوتی به متن 📝"]: - user_states[str_chat_id]["mode"] = "stt_waiting_for_audio" - await send_with_keyboard(client, chat_id, "📝 شما وارد بخش **تبدیل صدا به متن** شدید.\n\nلطفاً فایل خود (ویس، آهنگ، ویدیو و...) را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True) - return - - if user_text_str in["/create_file", "ساخت فایل 📄"]: - user_states[str_chat_id]["mode"] = "create_file_waiting_for_topic" - await send_with_keyboard(client, chat_id, "📄 شما وارد بخش **ساخت فایل** شدید.\n\nلطفاً موضوع مقاله‌ای که می‌خواهید را کامل بفرستید.\nمثال: نحوه مدیریت زمان\n\nهوش مصنوعی یک مقاله کامل و طولانی نوشته و در نهایت فایل PDF و Word آن را به شما تحویل می‌دهد.\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True) - return - - current_mode = user_states[str_chat_id].get("mode") - - if current_mode is None: - if is_file: pass - elif user_text_str: await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا از کیبورد پایین، بخش مورد نظرتان را انتخاب کنید:", True) - return - - elif current_mode == "waiting_for_referral_code": - if user_text_str: - normalized_code = to_english_digits(user_text_str).strip() - - if not normalized_code.isdigit() or len(normalized_code) != 8: - await send_with_keyboard(client, chat_id, "❌ کد وارد شده نامعتبر است. لطفاً یک کد 8 رقمی صحیح بفرستید.", False) - return - - inviter_id = find_user_by_referral_code(normalized_code) - - if not inviter_id: - await send_with_keyboard(client, chat_id, "❌ کد هدیه یافت نشد. لطفاً کد را با دقت بررسی کنید.", False) - return - - if str(inviter_id) == str(str_chat_id): - await send_with_keyboard(client, chat_id, "❌ شما نمی‌توانید کد خودتان را ثبت کنید!", False) - return - - user_states[str_chat_id]["mode"] = None - - user_credits_db[str_chat_id]["used_referral"] = True - user_credits_db[str_chat_id]["tts"] = user_credits_db[str_chat_id].get("tts", 0) + 10 - - user_credits_db[inviter_id]["invited_count"] = user_credits_db[inviter_id].get("invited_count", 0) + 1 - current_invites = user_credits_db[inviter_id]["invited_count"] - - if current_invites > 0 and current_invites % 10 == 0: - inviter_data = user_credits_db[inviter_id] - inviter_data["is_premium"] = True - now = datetime.datetime.now() - if inviter_data.get("expire_date"): - try: - current_exp = datetime.datetime.fromisoformat(inviter_data["expire_date"]) - if current_exp > now: - new_exp = current_exp + datetime.timedelta(days=3) - else: - new_exp = now + datetime.timedelta(days=3) - except: - new_exp = now + datetime.timedelta(days=3) - else: - new_exp = now + datetime.timedelta(days=3) - - inviter_data["expire_date"] = new_exp.isoformat() - inviter_data["chat"] = 999999 - inviter_data["image"] = 20 - inviter_data["edit_image"] = 10 - inviter_data["podcast"] = 999999 - inviter_data["tts"] = 999999 - inviter_data["file"] = 999999 - inviter_data["stt"] = 999999 - - save_db(user_credits_db) - try: - msg_text = f"🎉 **تبریک ویژه!**\nیک دوست کد هدیه شما را ثبت کرد.\nتعداد کل دعوت‌های شما: {current_invites} نفر\n\n🎁 **هدیه شما:** ۳ روز اشتراک پرو نامحدود به حساب شما افزوده شد!" - asyncio.create_task(send_with_keyboard(client, inviter_id, msg_text, True)) - except: pass - else: - save_db(user_credits_db) - remains = 10 - (current_invites % 10) - try: - msg_text = f"🎉 **تبریک!**\nیک دوست کد هدیه شما را ثبت کرد.\nتعداد کل دعوت‌های شما: {current_invites} نفر\n\n⏳ با دعوت {remains} نفر دیگر، ۳ روز اشتراک رایگان دریافت می‌کنید!" - asyncio.create_task(send_with_keyboard(client, inviter_id, msg_text, True)) - except: pass - - await send_with_keyboard(client, chat_id, "✅ کد هدیه با موفقیت ثبت شد!\n🎁 **10 سهمیه تبدیل رایگان متن به صدا** به حساب شما اضافه گردید.", True) - return - - elif current_mode == "chat": - if is_file: - await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False) - try: - file_bytes = await helper_download_file(client, msg_obj) - user_states[str_chat_id]["file_bytes"] = file_bytes - user_states[str_chat_id]["file_name"] = file_name - user_states[str_chat_id]["mode"] = "chat_waiting_for_prompt" - await send_with_keyboard(client, chat_id, "✅ فایل با موفقیت دریافت شد.\n\nحالا لطفاً متنی بفرستید و بگویید **چه کاری با این فایل انجام دهم؟**\n(مثلاً: این تصویر را توصیف کن، یا متن این سند را خلاصه کن)", False) - except Exception as dl_err: - await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False) - elif user_text_str: - asyncio.create_task(process_gemini(client, chat_id, user_text_str)) - return - - elif current_mode == "chat_waiting_for_prompt": - if user_text_str: - saved_bytes = user_states[str_chat_id].get("file_bytes") - saved_name = user_states[str_chat_id].get("file_name", "file.jpeg") - user_states[str_chat_id]["mode"] = "chat" - user_states[str_chat_id]["file_bytes"] = None - asyncio.create_task(process_gemini(client, chat_id, user_text_str, file_bytes=saved_bytes, file_name=saved_name)) - else: - await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False) - return - - elif current_mode == "image_waiting_for_text": - if user_text_str: - user_states[str_chat_id]["text"] = user_text_str - user_states[str_chat_id]["mode"] = "image_waiting_for_size" - - size_menu = """🖼 **لطفاً ابعاد تصویر خود را انتخاب کنید:** - -1️⃣ `1:1` (مربع) - مناسب پروفایل ⬛ -2️⃣ `9:16` (عمودی) - مناسب استوری و پس‌زمینه گوشی 📱 -3️⃣ `16:9` (افقی) - مناسب دسکتاپ و ویدیو 🖥️ -4️⃣ `4:3` (استاندارد) - مناسب چاپ و پست 📸 - -(لطفاً فقط عدد 1 تا 4 را بفرستید)""" - await send_with_keyboard(client, chat_id, size_menu, False) - else: - await send_with_keyboard(client, chat_id, "⚠️ لطفاً ایده عکس خود را به صورت متنی بفرستید.", False) - return - - elif current_mode == "image_waiting_for_size": - normalized_choice = to_english_digits(user_text_str).strip() - if normalized_choice in["1", "2", "3", "4"]: - saved_text = user_states[str_chat_id].get("text", "") - user_states[str_chat_id]["mode"] = None - asyncio.create_task(process_image(client, chat_id, saved_text, normalized_choice)) - else: - await send_with_keyboard(client, chat_id, "❌ عدد وارد شده نامعتبر است! لطفاً فقط یکی از اعداد 1 تا 4 را بفرستید.", False) - return - - elif current_mode == "image_edit_waiting_for_image": - if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا یک عکس ارسال کنید.", False) - await send_with_keyboard(client, chat_id, "📥 در حال دانلود عکس...", False) - try: - file_bytes = await helper_download_file(client, msg_obj) - user_states[str_chat_id]["file_bytes"] = file_bytes - user_states[str_chat_id]["mode"] = "image_edit_waiting_for_prompt" - await send_with_keyboard(client, chat_id, "✅ عکس با موفقیت دریافت شد.\n\nحالا دستور خود را به صورت متنی تایپ کنید.\nمثال: یک کلاه قرمز روی سر این گربه بگذار.", False) - except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت عکس!\n{str(dl_err)}", False) - return - - elif current_mode == "image_edit_waiting_for_prompt": - if user_text_str: - saved_bytes = user_states[str_chat_id].get("file_bytes") - user_states[str_chat_id]["mode"] = None - user_states[str_chat_id]["file_bytes"] = None - asyncio.create_task(process_image_edit(client, chat_id, saved_bytes, user_text_str)) - else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False) - return - - elif current_mode == "tts_waiting_for_text": - if user_text_str: - if len(user_text_str) > 2500: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً متنی کوتاه‌تر از 2500 کاراکتر بفرستید.", False) - user_states[str_chat_id]["text"] = user_text_str - user_states[str_chat_id]["mode"] = "tts_waiting_for_speaker" - - speakers_menu = """✅ متن شما ذخیره شد. -لطفاً **شماره** گوینده مورد نظر خود را بفرستید: -1. شهاب | 2. آوا | 3. نوید -4. آرمان | 5. مهسا | 6. دانا -7. سامان | 8. آرش | 9. شبنم -10. سحر | 11. مریم | 12. بهرام -13. نیکان| 14. فرناز | 15. سارا -16. مانی | 17. آرتین | 18. دلنواز -19. روژان | 20. امید | 21. بردیا -22. ترانه | 23. نیکو | 24. هستی -25. کامیار| 26. کیانوش| 27. پویا -28. مهتاب | 29. سام | 30. لیدا""" - await send_with_keyboard(client, chat_id, speakers_menu, False) - return - - elif current_mode == "tts_waiting_for_speaker": - normalized_text = to_english_digits(user_text_str) - - if normalized_text.isdigit() and normalized_text in SPEAKERS: - spk_name, spk_id = SPEAKERS[normalized_text] - txt = user_states[str_chat_id]["text"] - user_states[str_chat_id]["mode"] = "tts_waiting_for_text" - asyncio.create_task(process_tts(client, chat_id, txt, spk_id, spk_name)) - else: - await send_with_keyboard(client, chat_id, "❌ شماره نامعتبر است! لطفاً فقط یک عدد بین 1 تا 30 بفرستید.", False) - return - - elif current_mode == "podcast_waiting_for_topic": - if user_text_str: asyncio.create_task(process_podcast(client, chat_id, user_text_str)) - else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً موضوع پادکست خود را به صورت متنی بفرستید.", False) - return - - elif current_mode == "stt_waiting_for_audio": - if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً یک فایل ارسال کنید.", False) - await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False) - try: - audio_bytes = await helper_download_file(client, msg_obj) - asyncio.create_task(process_stt(client, chat_id, audio_bytes, file_name)) - except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دانلود فایل!\n{str(dl_err)}", False) - return - - elif current_mode == "file_waiting_for_file": - if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا یک فایل ارسال کنید.", False) - await send_with_keyboard(client, chat_id, "📥 در حال دریافت فایل...", False) - try: - file_bytes = await helper_download_file(client, msg_obj) - user_states[str_chat_id]["file_bytes"] = file_bytes - user_states[str_chat_id]["file_name"] = file_name - user_states[str_chat_id]["mode"] = "file_waiting_for_prompt" - await send_with_keyboard(client, chat_id, "✅ فایل با موفقیت دریافت شد.\n\nحالا لطفاً متنی بگویید **چگونه تحلیل شود؟**\n(می‌توانید سوال خاصی بپرسید یا فقط بخواهید فایل را توضیح دهد)", False) - except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False) - return - - elif current_mode == "file_waiting_for_prompt": - if user_text_str: - saved_bytes = user_states[str_chat_id].get("file_bytes") - saved_name = user_states[str_chat_id].get("file_name", "file.jpeg") - user_states[str_chat_id]["mode"] = "file_waiting_for_file" - asyncio.create_task(process_file_analysis(client, chat_id, saved_bytes, saved_name, user_text_str)) - else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False) - return - - elif current_mode == "create_file_waiting_for_topic": - if user_text_str: - asyncio.create_task(process_create_file(client, chat_id, user_text_str)) - else: - await send_with_keyboard(client, chat_id, "⚠️ لطفاً موضوع مقاله خود را به صورت متنی بفرستید.", False) - return - - # =============================================== - # بخش‌های تغییر صدا و کلون صدا - # =============================================== - elif current_mode == "vc_waiting_for_voice": - if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً یک فایل صوتی یا ویس ارسال کنید.", False) - await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل صوتی شما...", False) - try: - file_bytes = await helper_download_file(client, msg_obj) - user_states[str_chat_id]["file_bytes"] = file_bytes - user_states[str_chat_id]["mode"] = "vc_waiting_for_model" - - model_menu = "✅ صدای شما دریافت شد.\n\nلطفا **شماره** مدلی که می‌خواهید صدایتان به آن تبدیل شود را ارسال کنید:\n\n" - for k, v in LEGACY_MODELS.items(): model_menu += f"{k}. {v['name']}\n" - model_menu += "➖➖➖➖➖➖➖➖\n" - for k, v in STANDARD_MODELS.items(): model_menu += f"{k}. {v['name']}\n" - - await send_with_keyboard(client, chat_id, model_menu, False) - except Exception as dl_err: - await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False) - return - - elif current_mode == "vc_waiting_for_model": - choice = to_english_digits(user_text_str).strip() - - if choice in STANDARD_MODELS: - if creds["voice_conv"] <= 0: return await send_with_keyboard(client, chat_id, "❌ سهمیه تغییر صدای شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False) - user_states[str_chat_id]["mode"] = None - model = STANDARD_MODELS[choice] - src_bytes = user_states[str_chat_id]["file_bytes"] - await send_with_keyboard(client, chat_id, "📥 در حال آماده‌سازی فایل‌ها...", False) - ref_bytes = await helper_download_url_to_bytes(model["ref"]) - if not ref_bytes: return await send_with_keyboard(client, chat_id, "❌ خطا در دسترسی به فایل صدای مدل.", False) - - asyncio.create_task(process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, model["name"], "voice_conv")) - - elif choice in LEGACY_MODELS: - if creds["voice_conv"] <= 0: return await send_with_keyboard(client, chat_id, "❌ سهمیه تغییر صدای شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False) - user_states[str_chat_id]["selected_model"] = choice - user_states[str_chat_id]["mode"] = "vc_waiting_for_gender" - - gender_msg = f"✅ مدل {LEGACY_MODELS[choice]['name']} انتخاب شد.\n\nبرای تنظیم دقیق فرکانس‌ها به ما بگویید صدایی که خودتان فرستادید صدای یک **مرد** است یا **زن**؟\n\n1. مرد 👨\n2. زن 👩" - await send_with_keyboard(client, chat_id, gender_msg, False) - else: - await send_with_keyboard(client, chat_id, "❌ شماره وارد شده صحیح نیست. لطفاً فقط عدد مدل را بفرستید.", False) - return - - elif current_mode == "vc_waiting_for_gender": - choice = to_english_digits(user_text_str).strip() - if choice not in ["1", "2"]: - return await send_with_keyboard(client, chat_id, "❌ لطفاً عدد 1 (مرد) یا 2 (زن) را ارسال کنید.", False) - - user_gender = "male" if choice == "1" else "female" - model_key = user_states[str_chat_id]["selected_model"] - model = LEGACY_MODELS[model_key] - target_gender = model["gender"] - - pitch = 0 - if target_gender == "female" and user_gender == "male": pitch = 12 - elif target_gender == "male" and user_gender == "female": pitch = -12 - - user_states[str_chat_id]["mode"] = None - src_bytes = user_states[str_chat_id]["file_bytes"] - asyncio.create_task(process_legacy_vc_job(client, chat_id, src_bytes, model["url"], pitch, model["name"])) - return - - elif current_mode == "clone_waiting_for_src": - if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً فایل صوتی صدای خودتان را بفرستید.", False) - await send_with_keyboard(client, chat_id, "📥 در حال دانلود صدای شما...", False) - try: - file_bytes = await helper_download_file(client, msg_obj) - user_states[str_chat_id]["file_bytes"] = file_bytes - user_states[str_chat_id]["mode"] = "clone_waiting_for_ref" - await send_with_keyboard(client, chat_id, "✅ صدای شما دریافت شد.\n\nحالا **فایل الگو (صدای شخصی که می‌خواهید شبیه‌سازی کنید)** را ارسال کنید.\n(پیشنهاد: فایل بدون نویز و موسیقی، بین ۳ تا ۱۰ ثانیه)", False) - except Exception as dl_err: - await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False) - return - - elif current_mode == "clone_waiting_for_ref": - if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً فایل صوتی الگوی هدف را بفرستید.", False) - await send_with_keyboard(client, chat_id, "📥 در حال دانلود صدای الگو...", False) - try: - ref_bytes = await helper_download_file(client, msg_obj) - - if creds["voice_clone"] <= 0: - return await send_with_keyboard(client, chat_id, "❌ سهمیه کلون کردن صدای شما تمام شده است. نیازمند تهیه اشتراک هستید.", False) - - user_states[str_chat_id]["mode"] = None - src_bytes = user_states[str_chat_id]["file_bytes"] - - asyncio.create_task(process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, "صدای اختصاصی (کلون شده)", "voice_clone")) - except Exception as dl_err: - await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False) - return - - except Exception: traceback.print_exc() + except Exception: + pass if __name__ == "__main__": - loop = asyncio.get_event_loop() - - # 🟢 تونل‌های پرسرعت: ارتقا از 32 کارگر به 256 کارگر برای پاسخگویی همزمان به هزاران نفر - loop.set_default_executor(concurrent.futures.ThreadPoolExecutor(max_workers=256)) - - # 🟢 روشن کردن سیستم پاکسازی هوشمند رم در پس‌زمینه - loop.create_task(ram_garbage_collector()) - - # استارت کردن وب‌سرور فلسک در یک Thread جداگانه (که وب‌هوک را تنظیم و پیام‌ها را دریافت می‌کند) - import threading threading.Thread(target=run_flask, daemon=True).start() - if bot_token: - print("🚀 ربات آلفا پرو (نسخه صنعتی + وب‌هوک + دیتابیس هوشمند + پاکسازی رم + 256 تونل همزمان) روشن شد...") - # جایگزینی bot.run() با روشن نگه داشتن حلقه بی‌نهایت - try: - loop.run_forever() - except KeyboardInterrupt: - pass + print("⚠️ وضعیت: در حال تخلیه صف پیام‌ها در دیتابیس v3... لطفاً تا توقف لاگ‌ها صبر کنید.") + bot.run() \ No newline at end of file