| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| def esc(text: str) -> str: |
| """ |
| Escape رموز HTML الخاصة الثلاثة. |
| يجب استدعاؤها على أي نص يأتي من مصدر خارجي |
| قبل إدراجه في رسالة HTML. |
| """ |
| if not text: |
| return "" |
| return ( |
| str(text) |
| .replace("&", "&") |
| .replace("<", "<") |
| .replace(">", ">") |
| ) |
|
|
|
|
| def b(text: str) -> str: |
| """نص عريض""" |
| return f"<b>{esc(text)}</b>" |
|
|
|
|
| def i(text: str) -> str: |
| """نص مائل""" |
| return f"<i>{esc(text)}</i>" |
|
|
|
|
| def u(text: str) -> str: |
| """نص تحته خط""" |
| return f"<u>{esc(text)}</u>" |
|
|
|
|
| def code(text: str) -> str: |
| """نص كـ code""" |
| return f"<code>{esc(text)}</code>" |
|
|
|
|
| def link(label: str, url: str) -> str: |
| """رابط مخفي""" |
| return f'<a href="{url}">{esc(label)}</a>' |
|
|
|
|
| def blockquote(text: str, expandable: bool = False) -> str: |
| """ |
| blockquote عادي أو قابل للتوسيع. |
| expandable=True: يُظهر زر "اقرأ المزيد" إذا كان الحجم كبيراً. |
| """ |
| if expandable: |
| return f"<blockquote expandable>{text}</blockquote>" |
| return f"<blockquote>{text}</blockquote>" |
|
|
|
|
| def row(*cells: str, sep: str = " | ") -> str: |
| """صف بيانات: المفتاح | القيمة""" |
| return sep.join(str(c) for c in cells if c) |
|
|
|
|
| |
| |
| |
|
|
| def build_game_info_message(details: dict, game_link_in_lib: str | None = None) -> str: |
| """ |
| بناء رسالة تفاصيل اللعبة الكاملة بتنسيق HTML. |
| |
| الترتيب: |
| 1. رابط الصورة (كرابط نصي → يولّد Preview تلقائياً) |
| 2. اسم اللعبة (عريض) |
| 3. معلومات أساسية داخل blockquote |
| 4. اللغات داخل blockquote expandable |
| 5. التصنيفات والخصائص داخل blockquote expandable |
| 6. القصة (3 أسطر) |
| """ |
| parts = [] |
|
|
| |
| cover_url = details.get("cover_url", "") |
| if cover_url: |
| parts.append(cover_url) |
| parts.append("") |
|
|
| |
| parts.append(f"🎮 <b>{esc(details.get('name', ''))}</b>") |
| parts.append("") |
|
|
| |
| basic_lines = [] |
| if details.get("release_date"): |
| basic_lines.append(f"• تاريخ الإصدار: {esc(details['release_date'])}") |
| if details.get("developer"): |
| basic_lines.append(f"• المطور: {esc(details['developer'])}") |
| if details.get("publisher") and details["publisher"] != details.get("developer"): |
| basic_lines.append(f"• الناشر: {esc(details['publisher'])}") |
| if details.get("platforms_str"): |
| basic_lines.append(f"• المنصات: {esc(details['platforms_str'])}") |
|
|
| |
| ur = details.get("user_rating", "") |
| uc = details.get("user_rating_count", 0) |
| cr = details.get("crit_rating", "") |
| cc = details.get("crit_rating_count", 0) |
| rating_line_parts = [] |
| if ur: |
| rating_line_parts.append(f"{esc(ur)} ⭐ (بناء على {esc(str(uc))} مستخدم)") |
| if cr: |
| rating_line_parts.append(f"{esc(cr)} 🌟 (بناء على {esc(str(cc))} نقاد)") |
| if rating_line_parts: |
| basic_lines.append(f"• التقييمات: {' — '.join(rating_line_parts)}") |
|
|
| |
| age_lines = details.get("age_rating_lines", []) |
| if age_lines: |
| basic_lines.append("• التصنيف العمري:") |
| for al in age_lines: |
| basic_lines.append(f" {esc(al)}") |
|
|
| if basic_lines: |
| parts.append(blockquote("\n".join(basic_lines))) |
| parts.append("") |
|
|
| |
| lang_sections = details.get("language_sections", {}) |
| if any(lang_sections.values()): |
| lang_lines = [] |
| if lang_sections.get("audio"): |
| lang_lines.append(f"🔊 <b>الصوت / الدبلجة:</b>") |
| lang_lines.append(esc(" • ".join(lang_sections["audio"]))) |
| if lang_sections.get("interface"): |
| lang_lines.append(f"🖥️ <b>الواجهة:</b>") |
| lang_lines.append(esc(" • ".join(lang_sections["interface"]))) |
| if lang_sections.get("subtitles"): |
| lang_lines.append(f"📝 <b>الترجمة النصية:</b>") |
| lang_lines.append(esc(" • ".join(lang_sections["subtitles"]))) |
|
|
| if lang_lines: |
| parts.append(f"<b>اللغات المدعومة:</b>") |
| parts.append(blockquote("\n".join(lang_lines), expandable=True)) |
| parts.append("") |
|
|
| |
| cat_lines = [] |
| if details.get("genres_ar"): |
| cat_lines.append(f"• الأنواع: {esc(details['genres_ar'])}") |
| if details.get("themes_ar"): |
| cat_lines.append(f"• المواضيع: {esc(details['themes_ar'])}") |
| if details.get("game_modes_ar"): |
| cat_lines.append(f"• أسلوب اللعب: {esc(details['game_modes_ar'])}") |
| if details.get("perspectives_ar"): |
| cat_lines.append(f"• منظور اللاعب: {esc(details['perspectives_ar'])}") |
| if details.get("engine"): |
| cat_lines.append(f"• المحرك: {esc(details['engine'])}") |
| if details.get("franchise"): |
| cat_lines.append(f"• السلسلة: {esc(details['franchise'])}") |
| if details.get("parent_game"): |
| cat_lines.append(f"• هو فرعي من: {esc(details['parent_game'])}") |
| if details.get("collection"): |
| cat_lines.append(f"• سلسلة أعمال: {esc(details['collection'])}") |
|
|
| if cat_lines: |
| parts.append(blockquote("\n".join(cat_lines), expandable=True)) |
| parts.append("") |
|
|
| |
| if details.get("summary"): |
| parts.append(f"<b>📖 نبذة عن القصة:</b>") |
| parts.append(esc(details["summary"])) |
|
|
| |
| if game_link_in_lib: |
| parts.append("") |
| parts.append(f"🎌 {link('شاهد التعريب في مكتبتنا', game_link_in_lib)}") |
|
|
| return "\n".join(p for p in parts) |
|
|
|
|
| def build_pc_specs_message(game_name: str, game_info: dict, pc_specs: dict) -> str: |
| """ |
| رسالة متطلبات تشغيل PC كاملة بتنسيق HTML. |
| كل حقل في blockquote منفصل حسب التصميم المطلوب. |
| """ |
| parts = [] |
|
|
| |
| parts.append(f"🎮 <b>{esc(game_name)}</b>") |
|
|
| |
| genres_raw = game_info.get("genres_ar", "") |
| if genres_raw: |
| tags = [] |
| for g in genres_raw.split("|"): |
| g = g.strip() |
| |
| tag_text = ''.join(c for c in g if c.isalpha() or c in ' _').strip().replace(' ', '_') |
| if tag_text: |
| tags.append(f"#{tag_text}") |
| if tags: |
| parts.append(" ".join(tags[:3])) |
|
|
| |
| age_lines = game_info.get("age_rating_lines", []) |
| if age_lines: |
| parts.append(f"🔞 تصنيف العمر: {esc(age_lines[0].split()[1] if len(age_lines[0].split()) > 1 else age_lines[0])}") |
| if game_info.get("release_date"): |
| parts.append(f"🗓️ الإصدار: {esc(game_info['release_date'])}") |
|
|
| parts.append("") |
|
|
| |
| min_specs = pc_specs.get("minimum", {}) |
| if min_specs: |
| parts.append(f"<b>🔹 الحد الأدنى (Minimum)</b>") |
|
|
| _SPEC_LABELS_AR = { |
| "OS": "نظام التشغيل", |
| "CPU": "المعالج (CPU)", |
| "RAM": "الذاكرة (RAM)", |
| "GPU": "كرت الشاشة (GPU)", |
| "DirectX": "DirectX", |
| "Storage": "التخزين", |
| "Network": "الشبكة", |
| "Sound": "كارت الصوت", |
| } |
|
|
| for key, val in min_specs.items(): |
| if val: |
| label = _SPEC_LABELS_AR.get(key, key) |
| parts.append(blockquote(f"{esc(label)} | {esc(str(val))}")) |
|
|
| |
| rec_specs = pc_specs.get("recommended", {}) |
| if rec_specs: |
| parts.append("") |
| parts.append(f"<b>🔹 الموصى بها (Recommended)</b>") |
|
|
| _SPEC_LABELS_AR = { |
| "OS": "نظام التشغيل", |
| "CPU": "المعالج (CPU)", |
| "RAM": "الذاكرة (RAM)", |
| "GPU": "كرت الشاشة (GPU)", |
| "DirectX": "DirectX", |
| "Storage": "التخزين", |
| "Network": "الشبكة", |
| "Sound": "كارت الصوت", |
| } |
|
|
| for key, val in rec_specs.items(): |
| if val: |
| label = _SPEC_LABELS_AR.get(key, key) |
| parts.append(blockquote(f"{esc(label)} | {esc(str(val))}")) |
|
|
| if not min_specs and not rec_specs: |
| parts.append(blockquote("لم يتم العثور على متطلبات رسمية لهذه اللعبة.")) |
|
|
| return "\n".join(parts) |
|
|
|
|
| def build_new_post_notification( |
| game_title: str, platform: str, |
| game_link: str, channel_username: str |
| ) -> str: |
| """رسالة إشعار تعريب جديد (HTML)""" |
| emoji = "🖥" if platform == "pc" else "🎮" if platform == "ps" else "📁" |
| plat_ar = "PC" if platform == "pc" else "PlayStation" if platform == "ps" else "عام" |
| ch_part = f"\n📢 @{esc(channel_username)}" if channel_username else "" |
| return ( |
| f"🔔 <b>تعريب جديد!</b>\n\n" |
| f"🎮 <b>{esc(game_title)}</b>\n" |
| f"{emoji} المنصة: {esc(plat_ar)}\n" |
| f"🔗 <a href='{game_link}'>شاهد التعريب</a>" |
| f"{ch_part}" |
| ) |
|
|
|
|
| def build_update_notification( |
| game_title: str, update_note: str, |
| game_link: str, channel_username: str |
| ) -> str: |
| """رسالة إشعار تحديث تعريب (HTML)""" |
| ch_part = f"\n📢 @{esc(channel_username)}" if channel_username else "" |
| note_part = f"\n✏️ {esc(update_note)}" if update_note else "" |
| return ( |
| f"🔄 <b>تم تحديث تعريب!</b>\n\n" |
| f"🎮 <b>{esc(game_title)}</b>" |
| f"{note_part}\n" |
| f"🔗 <a href='{game_link}'>شاهد التحديث</a>" |
| f"{ch_part}" |
| ) |
|
|