Spaces:
Sleeping
Sleeping
| import discord | |
| from discord.ext import commands, tasks | |
| from discord import app_commands | |
| from discord.ui import Button, View, Select | |
| import asyncio | |
| import random | |
| import os | |
| from datetime import datetime | |
| # ======================================================= | |
| # الإعدادات الأساسية (الثوابت) | |
| # ======================================================= | |
| TARGET_GUILD_ID = 1463693244897693779 # أيدي السيرفر المسموح | |
| OWNER_ROLE_ID = 1492516779598155877 # أيدي رتبة المالك للتحكم | |
| # ======================================================= | |
| # الأذكار والتذكيرات (القائمة الكاملة) | |
| # ======================================================= | |
| REMINDERS = [ | |
| "🌸 سبحان الله وبحمده، سبحان الله العظيم", | |
| "✨ اللهم صل وسلم على نبينا محمد", | |
| "📿 أستغفر الله العظيم وأتوب إليه", | |
| "🕊️ لا حول ولا قوة إلا بالله", | |
| "🌿 سبحان الله، والحمد لله، ولا إله إلا الله، والله أكبر", | |
| "🌸 سبحان الله", "✨ الحمد لله", "📿 الله أكبر", "🕊️ لا إله إلا الله", | |
| "🌿 اللهم اغفر لي", "🌸 اللهم ارحمني", "✨ اللهم تب علي", "📿 اللهم ارزقني", | |
| "🕊️ حسبي الله ونعم الوكيل", "🌿 توكلت على الله", "🌸 رب اغفر لي ولوالدي", | |
| "✨ اللهم اجعلني من الصالحين", "📿 اللهم اهدني", "🕊️ اللهم ثبتني", | |
| "🌿 اللهم ارزقني الجنة", "🌸 اللهم أجرني من النار", "✨ يا رب", "📿 يا الله", | |
| "🕊️ يا حي يا قيوم برحمتك أستغيث", "🌿 اللهم إني أسألك العافية", | |
| "🌸 اللهم إني أسألك الهدى والتقى", "✨ اللهم إني أعوذ بك من الهم والحزن", | |
| "📿 اللهم إني أعوذ بك من العجز والكسل", "🕊️ اللهم إني أعوذ بك من الجبن والبخل", | |
| "🌿 اللهم إني أعوذ بك من غلبة الدين وقهر الرجال", "🌸 اللهم اجعل القرآن ربيع قلبي", | |
| "✨ اللهم نور قلبي", "📿 اللهم طهر قلبي", "🕊️ اللهم أصلح حالي", | |
| "🌿 اللهم اجعلني من الذاكرين", "🌸 اللهم اجعلني من الشاكرين", | |
| "✨ اللهم اجعلني من الصابرين", "📿 اللهم اجعلني من التوابين", | |
| "🕊️ اللهم اجعلني من المتقين", "🌿 اللهم اجعلني من المحسنين", | |
| "🌸 اللهم ارزقني الإخلاص", "✨ اللهم ارزقني الصدق", "📿 اللهم ارزقني حسن الخاتمة", | |
| "🕊️ اللهم اجعل قبري روضة من رياض الجنة", "🌿 اللهم اجعلني من أهل الجنة", | |
| "🌸 اللهم ارزقني الفردوس الأعلى", "✨ اللهم لا تحرمني من رحمتك", | |
| "📿 اللهم لا تكلني إلى نفسي", "🕊️ اللهم ارحم موتانا", "🌿 اللهم اشف مرضانا", | |
| "🌸 اللهم فرج همومنا", "✨ اللهم اقض ديوننا", "📿 اللهم وسع رزقنا", | |
| "🕊️ اللهم بارك لنا", "🌿 اللهم احفظنا", "🌸 اللهم انصرنا", | |
| "✨ اللهم اجعلنا من عبادك الصالحين", "📿 اللهم ارزقنا رضاك", | |
| "🕊️ اللهم اجعلنا من أهل الذكر", "🌿 اللهم ارزقنا حبك", "🌸 اللهم ارزقنا حب نبيك", | |
| "✨ اللهم اجعلنا من المتوكلين", "📿 اللهم ارزقنا الصبر", "🕊️ اللهم ارزقنا اليقين", | |
| "🌿 اللهم اجعلنا من الذاكرين الله كثيرا", "🌸 اللهم اجعلنا من القانتين", | |
| "✨ اللهم ارزقنا حسن الظن بك", "📿 اللهم اجعلنا من المقبولين", | |
| "🕊️ اللهم ارزقنا لذة النظر إلى وجهك", "🌿 اللهم اجعلنا من الفائزين", | |
| "🌸 اللهم اجعلنا من أهل الفردوس", "✨ اللهم اجعلنا من عتقائك من النار", | |
| "📿 اللهم اجعلنا من المستغفرين", "🕊️ اللهم اجعلنا من الراجعين إليك", | |
| "🌿 اللهم اجعلنا من المنيبين", "🌸 اللهم ارزقنا التوبة النصوح", | |
| "✨ اللهم تقبل منا", "📿 اللهم اغفر لنا", "🕊️ اللهم ارحمنا", "🌿 اللهم تب علينا", | |
| "🌸 اللهم اهدنا", "✨ اللهم احفظنا من كل سوء", "📿 اللهم اجعلنا من الصالحين", | |
| "🕊️ اللهم ارزقنا الجنة بغير حساب", "🌿 اللهم اجعلنا من أهل القرآن", | |
| "🌸 اللهم اجعل القرآن شفيعاً لنا", "✨ اللهم اجعل القرآن حجة لنا", | |
| "📿 اللهم اجعلنا من الذين يستمعون القول فيتبعون أحسنه", | |
| "🕊️ اللهم اجعلنا من أولي الألباب", "🌿 اللهم ارزقنا العلم النافع", | |
| "🌸 اللهم ارزقنا العمل الصالح", "✨ اللهم ارزقنا الإيمان الكامل", | |
| "📿 اللهم اجعلنا من أهل التقوى", "🕊️ اللهم اجعلنا من الصادقين", | |
| "🌿 اللهم اجعلنا من المخلصين", "🌸 اللهم اجعلنا من عبادك المقربين", | |
| "✨ اللهم اجعلنا من الذاكرين الشاكرين", "📿 اللهم اجعلنا من الصابرين المحتسبين" | |
| ] | |
| # ======================================================= | |
| # قائمة 30 قارئ (بدون اختصار) | |
| # ======================================================= | |
| RECITERS = [ | |
| {"name": "إذاعة تلاوات منوعة", "desc": "بث مباشر لتلاوات مختارة", "url": "https://qurango.net/radio/tarateel"}, | |
| {"name": "عبدالباسط عبدالصمد", "desc": "تلاوة مجودة بصوت خاشع", "url": "https://qurango.net/radio/abdulbasit_abdulsamad_mjawwad"}, | |
| {"name": "مشاري العفاسي", "desc": "تلاوة هادئة ومريحة", "url": "https://qurango.net/radio/mishary_alafasi"}, | |
| {"name": "ياسر الدوسري", "desc": "تلاوات مميزة من الحرم", "url": "https://qurango.net/radio/yasser_aldosari"}, | |
| {"name": "ماهر المعيقلي", "desc": "تلاوة عذبة وشهيرة", "url": "https://qurango.net/radio/maher_al_muaiqly"}, | |
| {"name": "أحمد بن علي العجمي", "desc": "تلاوة خاشعة", "url": "https://qurango.net/radio/ahmad_alajmy"}, | |
| {"name": "سعد الغامدي", "desc": "تلاوة مريحة للقلب", "url": "https://qurango.net/radio/saad_alghamidi"}, | |
| {"name": "سعود الشريم", "desc": "من تلاوات الحرم المكي", "url": "https://qurango.net/radio/saud_alshuraim"}, | |
| {"name": "عبدالرحمن السديس", "desc": "تلاوة الحرم المكي الشريف", "url": "https://qurango.net/radio/abdulrahman_alsudaes"}, | |
| {"name": "محمود خليل الحصري", "desc": "تلاوة مرتلة دقيقة", "url": "https://qurango.net/radio/mahmoud_khalil_alhussary"}, | |
| {"name": "علي عبدالله جابر", "desc": "تلاوة مميزة", "url": "https://qurango.net/radio/ali_jaber"}, | |
| {"name": "محمد أيوب", "desc": "تلاوة حجازية", "url": "https://qurango.net/radio/mohammed_ayyub"}, | |
| {"name": "محمد صديق المنشاوي", "desc": "تلاوة خاشعة جداً", "url": "https://qurango.net/radio/mohammed_siddiq_alminshawi"}, | |
| {"name": "أبوبكر الشاطري", "desc": "تلاوة هادئة", "url": "https://qurango.net/radio/abubakr_alshatri"}, | |
| {"name": "عبدالله بصفر", "desc": "تلاوة عذبة", "url": "https://qurango.net/radio/abdullah_basfer"}, | |
| {"name": "عبدالمحسن القاسم", "desc": "إمام المسجد النبوي", "url": "https://qurango.net/radio/abdulmohsin_alqasim"}, | |
| {"name": "عبدالودود حنيف", "desc": "تلاوة رائعة", "url": "https://qurango.net/radio/abdulwadood_haneef"}, | |
| {"name": "علي عبدالرحمن الحذيفي", "desc": "من تلاوات المسجد النبوي", "url": "https://qurango.net/radio/ali_alhuthaifi"}, | |
| {"name": "فارس عباد", "desc": "تلاوة مؤثرة", "url": "https://qurango.net/radio/fares_abbad"}, | |
| {"name": "محمد جبريل", "desc": "تلاوة مشهورة", "url": "https://qurango.net/radio/mohammed_jibreel"}, | |
| {"name": "محمد الطبلاوي", "desc": "من كبار القراء", "url": "https://qurango.net/radio/mohammad_altablaway"}, | |
| {"name": "محمود علي البنا", "desc": "تلاوة كلاسيكية", "url": "https://qurango.net/radio/mahmoud_ali__albanna"}, | |
| {"name": "مصطفى إسماعيل", "desc": "تلاوة مجودة", "url": "https://qurango.net/radio/mustafa_ismail"}, | |
| {"name": "هاني الرفاعي", "desc": "تلاوة بكائية", "url": "https://qurango.net/radio/hani_arrifai"}, | |
| {"name": "يحيى حوا", "desc": "تلاوة هادئة", "url": "https://qurango.net/radio/yahya_hawwa"}, | |
| {"name": "خالد القحطاني", "desc": "تلاوة خاشعة", "url": "https://qurango.net/radio/khalid_alqahtani"}, | |
| {"name": "صلاح البدير", "desc": "إمام المسجد النبوي", "url": "https://qurango.net/radio/salah_albudair"}, | |
| {"name": "صلاح بو خاطر", "desc": "تلاوة ندية", "url": "https://qurango.net/radio/salah_bukhatir"}, | |
| {"name": "عبدالرشيد صوفي", "desc": "تلاوة مميزة بروايات", "url": "https://qurango.net/radio/abdulrasheed_soufi"}, | |
| {"name": "عبدالله عواد الجهني", "desc": "إمام الحرم المكي", "url": "https://qurango.net/radio/abdullah_aljuhani"} | |
| ] | |
| # --- متغيرات التحكم بالبث --- | |
| current_reciter_index = 0 | |
| current_volume = 1.0 | |
| # إعدادات أكثر أماناً لتجنب حظر IP من السيرفرات الصوتية | |
| FFMPEG_OPTIONS = { | |
| 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', | |
| 'options': '-vn -sn -dn -bufsize 500k -max_muxing_queue_size 1024' | |
| } | |
| # ======================================================= | |
| # تجهيز واجهة الأزرار (Control Panel) | |
| # ======================================================= | |
| class ReciterSelect(Select): | |
| def __init__(self): | |
| options = [] | |
| for i, r in enumerate(RECITERS[:25]): | |
| options.append(discord.SelectOption(label=r['name'], description=r['desc'][:50], value=str(i))) | |
| super().__init__(placeholder="اختر القارئ من القائمة السريعة...", min_values=1, max_values=1, options=options, custom_id="select_reciter") | |
| async def callback(self, interaction: discord.Interaction): | |
| role = interaction.guild.get_role(OWNER_ROLE_ID) | |
| if role not in interaction.user.roles: | |
| return await interaction.response.send_message("❌ لا تملك صلاحية.", ephemeral=True) | |
| global current_reciter_index | |
| current_reciter_index = int(self.values[0]) | |
| vc = interaction.guild.voice_client | |
| if vc: await play_current_reciter(vc) | |
| try: | |
| await interaction.response.send_message(f"🎙️ تم التغيير إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True) | |
| await log_event(interaction.guild, f"🎙️ **{interaction.user.name}** قام بتغيير القارئ إلى: {RECITERS[current_reciter_index]['name']}") | |
| except: pass | |
| class ControlPanelView(View): | |
| def __init__(self): | |
| super().__init__(timeout=None) | |
| self.add_item(ReciterSelect()) | |
| async def check_owner(self, interaction: discord.Interaction): | |
| role = interaction.guild.get_role(OWNER_ROLE_ID) | |
| if role and role in interaction.user.roles: | |
| return True | |
| try: | |
| await interaction.response.send_message("❌ عذراً، لا تملك الصلاحية (رتبة المالك المطلوبة).", ephemeral=True) | |
| except: pass | |
| return False | |
| async def toggle_btn(self, interaction: discord.Interaction, button: Button): | |
| if not await self.check_owner(interaction): return | |
| vc = interaction.guild.voice_client | |
| try: | |
| if vc: | |
| if vc.is_paused(): | |
| vc.resume() | |
| await interaction.response.send_message("▶️ تم استئناف البث.", ephemeral=True) | |
| await log_event(interaction.guild, f"▶️ **{interaction.user.name}** قام باستئناف البث.") | |
| elif vc.is_playing(): | |
| vc.pause() | |
| await interaction.response.send_message("⏸️ تم إيقاف البث مؤقتاً.", ephemeral=True) | |
| await log_event(interaction.guild, f"⏸️ **{interaction.user.name}** قام بإيقاف البث يدوياً.") | |
| else: | |
| await play_current_reciter(vc) | |
| await interaction.response.send_message("▶️ تم تشغيل البث.", ephemeral=True) | |
| else: | |
| await interaction.response.send_message("❌ البوت غير متصل.", ephemeral=True) | |
| except: pass | |
| async def next_btn(self, interaction: discord.Interaction, button: Button): | |
| if not await self.check_owner(interaction): return | |
| global current_reciter_index | |
| current_reciter_index = (current_reciter_index + 1) % len(RECITERS) | |
| vc = interaction.guild.voice_client | |
| if vc: await play_current_reciter(vc) | |
| try: | |
| await interaction.response.send_message(f"⏭️ تم الانتقال إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True) | |
| await log_event(interaction.guild, f"⏭️ **{interaction.user.name}** انتقل للقارئ: {RECITERS[current_reciter_index]['name']}") | |
| except: pass | |
| async def prev_btn(self, interaction: discord.Interaction, button: Button): | |
| if not await self.check_owner(interaction): return | |
| global current_reciter_index | |
| current_reciter_index = (current_reciter_index - 1) % len(RECITERS) | |
| vc = interaction.guild.voice_client | |
| if vc: await play_current_reciter(vc) | |
| try: | |
| await interaction.response.send_message(f"⏮️ تم الرجوع إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True) | |
| await log_event(interaction.guild, f"⏮️ **{interaction.user.name}** رجع للقارئ: {RECITERS[current_reciter_index]['name']}") | |
| except: pass | |
| class QuranBot(commands.Bot): | |
| def __init__(self): | |
| intents = discord.Intents.default() | |
| intents.message_content = True | |
| intents.voice_states = True | |
| intents.guilds = True | |
| super().__init__(command_prefix="!", intents=intents) | |
| async def setup_hook(self): | |
| self.add_view(ControlPanelView()) | |
| await self.tree.sync() | |
| print("✅ تم مزامنة الأوامر والأزرار بنجاح.") | |
| bot = QuranBot() | |
| # ======================================================= | |
| # دوال مساعدة وحماية من الحظر | |
| # ======================================================= | |
| async def log_event(guild: discord.Guild, message: str): | |
| log_channel = discord.utils.get(guild.text_channels, name="📜-سجلات-البوت") | |
| time_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| if log_channel: | |
| try: | |
| await log_channel.send(f"`[{time_now}]` {message}") | |
| except: pass # منع توقف البوت إذا رفض ديسكورد إرسال الرسالة بسبب Rate Limit | |
| print(f"[{time_now}] {message}") | |
| def is_owner_and_in_control(): | |
| async def predicate(interaction: discord.Interaction): | |
| if not interaction.guild: | |
| try: await interaction.response.send_message("❌ هذا الأمر مخصص للاستخدام داخل السيرفر فقط.", ephemeral=True) | |
| except: pass | |
| return False | |
| if interaction.channel.name != "🛠️ غرفة التحكم (للمالك)": | |
| try: await interaction.response.send_message("❌ عذراً، لا يمكنك استخدام الأوامر إلا داخل 🛠️ غرفة التحكم.", ephemeral=True) | |
| except: pass | |
| return False | |
| role = interaction.guild.get_role(OWNER_ROLE_ID) | |
| if role and role in interaction.user.roles: | |
| return True | |
| try: await interaction.response.send_message("❌ عذراً، لا تملك الصلاحية (رتبة المالك المطلوبة).", ephemeral=True) | |
| except: pass | |
| return False | |
| return app_commands.check(predicate) | |
| async def play_current_reciter(vc: discord.VoiceClient): | |
| try: | |
| if vc.is_playing() or vc.is_paused(): | |
| vc.stop() | |
| await asyncio.sleep(1) # تأخير بسيط لحماية الـ API من ضغط الطلبات | |
| reciter = RECITERS[current_reciter_index] | |
| audio_source = discord.FFmpegPCMAudio(reciter["url"], **FFMPEG_OPTIONS) | |
| vc.play(discord.PCMVolumeTransformer(audio_source, volume=current_volume)) | |
| except Exception as e: | |
| print(f"⚠️ خطأ أثناء التشغيل (تم تجاوزه): {e}") | |
| # ======================================================= | |
| # هندسة القنوات والأحداث | |
| # ======================================================= | |
| async def on_ready(): | |
| # حالة مستقرة وآمنة تظهر للمستخدمين | |
| await bot.change_presence(status=discord.Status.online, activity=discord.Activity(type=discord.ActivityType.listening, name="القرآن الكريم 24/7 🎧")) | |
| print(f"🤖 البوت {bot.user} متصل بشبكة ديسكورد بنجاح وهو الآن (Online).") | |
| # فحص هادئ للسيرفرات | |
| for guild in bot.guilds: | |
| if guild.id != TARGET_GUILD_ID: | |
| print(f"👢 جاري مغادرة سيرفر غريب بهدوء: {guild.name}") | |
| await asyncio.sleep(2) # تأخير لمنع الطرد المتعدد السريع | |
| try: await guild.leave() | |
| except: pass | |
| else: | |
| await check_and_create_channels(guild) | |
| await log_event(guild, "🚀 **تم إقلاع النظام والبوت متصل الآن.**") | |
| # تشغيل نبض القلب (فحص كل 5 دقائق) | |
| if not heartbeat_task.is_running(): | |
| heartbeat_task.start() | |
| async def on_guild_join(guild): | |
| print(f"📥 البوت دخل سيرفر جديد: {guild.name}") | |
| await asyncio.sleep(2) # حماية API ديسكورد | |
| if guild.id != TARGET_GUILD_ID: | |
| try: await guild.leave() | |
| except: pass | |
| else: | |
| await check_and_create_channels(guild) | |
| await log_event(guild, "🚀 **تم إضافة البوت للسيرفر الصحيح وبدء التشغيل!**") | |
| async def check_and_create_channels(guild): | |
| category_name = "إذاعة القرآن الكريم" | |
| broadcast_vc_name = "🎧 استماع القرآن (بث عام)" | |
| control_vc_name = "🛠️ غرفة التحكم (للمالك)" | |
| log_channel_name = "📜-سجلات-البوت" | |
| category = discord.utils.get(guild.categories, name=category_name) | |
| broadcast_vc = discord.utils.get(guild.voice_channels, name=broadcast_vc_name) | |
| control_vc = discord.utils.get(guild.voice_channels, name=control_vc_name) | |
| log_tc = discord.utils.get(guild.text_channels, name=log_channel_name) | |
| owner_role = guild.get_role(OWNER_ROLE_ID) | |
| try: | |
| if not category: | |
| category = await guild.create_category(category_name) | |
| await asyncio.sleep(1) | |
| if not log_tc: | |
| overwrites_log = { | |
| guild.default_role: discord.PermissionOverwrite(view_channel=False), | |
| bot.user: discord.PermissionOverwrite(view_channel=True, send_messages=True, read_message_history=True) | |
| } | |
| if owner_role: overwrites_log[owner_role] = discord.PermissionOverwrite(view_channel=True, read_message_history=True) | |
| log_tc = await guild.create_text_channel(log_channel_name, category=category, overwrites=overwrites_log) | |
| await asyncio.sleep(1) | |
| if not broadcast_vc: | |
| overwrites_broadcast = { | |
| guild.default_role: discord.PermissionOverwrite( | |
| view_channel=True, connect=True, read_message_history=True, | |
| speak=False, send_messages=False, add_reactions=False, stream=False | |
| ), | |
| bot.user: discord.PermissionOverwrite(connect=True, speak=True, send_messages=True, administrator=True) | |
| } | |
| broadcast_vc = await guild.create_voice_channel(broadcast_vc_name, category=category, overwrites=overwrites_broadcast) | |
| await asyncio.sleep(1) | |
| if not control_vc: | |
| overwrites_control = { | |
| guild.default_role: discord.PermissionOverwrite(view_channel=False, connect=False), | |
| bot.user: discord.PermissionOverwrite(view_channel=True, connect=True, send_messages=True) | |
| } | |
| if owner_role: overwrites_control[owner_role] = discord.PermissionOverwrite(view_channel=True, connect=True, send_messages=True) | |
| control_vc = await guild.create_voice_channel(control_vc_name, category=category, overwrites=overwrites_control, user_limit=99) | |
| embed = discord.Embed( | |
| title="🎛️ لوحة تحكم الإذاعة الذكية", | |
| description="استخدم الأزرار أدناه للتحكم السريع في البث، أو اختر القارئ من القائمة المنسدلة.\n\n*ملاحظة: يمكنك أيضاً استخدام الأوامر مثل `/volume` هنا فقط.*", | |
| color=discord.Color.dark_theme() | |
| ) | |
| embed.set_footer(text="SaaS Pro Dashboard") | |
| await control_vc.send(embed=embed, view=ControlPanelView()) | |
| # اتصال البوت | |
| if not guild.voice_client: | |
| vc = await broadcast_vc.connect() | |
| await asyncio.sleep(2) # استقرار الاتصال | |
| await play_current_reciter(vc) | |
| human_members = sum(1 for m in broadcast_vc.members if not m.bot) | |
| if human_members == 0: | |
| vc.pause() | |
| await log_event(guild, "⏸️ القناة فارغة، تم وضع البث في وضع الاستعداد (لتجنب ضغط الشبكة).") | |
| else: | |
| await log_event(guild, "▶️ تم البدء بالبث لوجود مستمعين في القناة.") | |
| except Exception as e: | |
| print(f"⚠️ تحذير أثناء إنشاء القنوات (قد يكون بسبب ضغط الطلبات): {e}") | |
| async def on_voice_state_update(member, before, after): | |
| if member.bot: return | |
| guild = member.guild | |
| if guild.id != TARGET_GUILD_ID: return | |
| bot_vc = guild.voice_client | |
| if not bot_vc: return | |
| try: | |
| # حالة دخول عضو | |
| if after.channel and after.channel.id == bot_vc.channel.id: | |
| if bot_vc.is_paused(): | |
| bot_vc.resume() | |
| await log_event(guild, f"▶️ استئناف البث التلقائي لدخول مستمع.") | |
| embed = discord.Embed( | |
| title="أهلاً بك في مجلس الذكر 🕊️", | |
| description=random.choice(REMINDERS), | |
| color=discord.Color.gold() | |
| ) | |
| embed.set_footer(text=f"القارئ الحالي: {RECITERS[current_reciter_index]['name']}") | |
| try: | |
| await after.channel.send(content=f"مرحباً بك {member.mention}", embed=embed, delete_after=60) | |
| except: pass | |
| # حالة خروج عضو | |
| elif before.channel and before.channel.id == bot_vc.channel.id: | |
| human_members = sum(1 for m in before.channel.members if not m.bot) | |
| if human_members == 0 and bot_vc.is_playing(): | |
| bot_vc.pause() | |
| except: pass # منع أي خطأ مفاجئ من إيقاف البوت | |
| # ======================================================= | |
| # مهمة الفحص الهادئ (Heartbeat) | |
| # ======================================================= | |
| async def heartbeat_task(): | |
| """هذه الدالة تفحص البوت كل 5 دقائق لضمان عدم فصله من ديسكورد""" | |
| try: | |
| guild = bot.get_guild(TARGET_GUILD_ID) | |
| if guild and guild.voice_client: | |
| human_members = sum(1 for m in guild.voice_client.channel.members if not m.bot) | |
| # إذا كان هناك ناس والصوت متوقف بالخطأ، نقوم بتشغيله | |
| if human_members > 0 and not guild.voice_client.is_playing() and not guild.voice_client.is_paused(): | |
| await play_current_reciter(guild.voice_client) | |
| await log_event(guild, "🔄 تم إعادة إنعاش البث التلقائي بواسطة نظام الحماية (Heartbeat).") | |
| except: pass | |
| # ======================================================= | |
| # أوامر السلاش (محصورة في غرفة التحكم) | |
| # ======================================================= | |
| async def play_radio(interaction: discord.Interaction): | |
| vc = interaction.guild.voice_client | |
| try: | |
| if not vc: return await interaction.response.send_message("❌ البوت غير متصل.", ephemeral=True) | |
| if vc.is_paused(): vc.resume() | |
| else: await play_current_reciter(vc) | |
| await interaction.response.send_message(f"▶️ تم التشغيل: **{RECITERS[current_reciter_index]['name']}**", ephemeral=True) | |
| await log_event(interaction.guild, f"▶️ تم التشغيل عبر أمر /play_radio") | |
| except: pass | |
| async def stop_radio(interaction: discord.Interaction): | |
| vc = interaction.guild.voice_client | |
| try: | |
| if vc and vc.is_playing(): | |
| vc.pause() | |
| await interaction.response.send_message("⏹️ تم الإيقاف.", ephemeral=True) | |
| await log_event(interaction.guild, f"⏹️ تم الإيقاف عبر أمر /stop_radio") | |
| else: | |
| await interaction.response.send_message("⚠️ متوقف بالفعل.", ephemeral=True) | |
| except: pass | |
| async def set_volume(interaction: discord.Interaction, level: int): | |
| global current_volume | |
| try: | |
| if level < 1 or level > 100: | |
| return await interaction.response.send_message("❌ يرجى إدخال رقم بين 1 و 100.", ephemeral=True) | |
| current_volume = level / 100.0 | |
| vc = interaction.guild.voice_client | |
| if vc and vc.source: vc.source.volume = current_volume | |
| await interaction.response.send_message(f"🔊 تم تغيير درجة الصوت إلى: **{level}%**", ephemeral=True) | |
| await log_event(interaction.guild, f"🔊 تم تغيير درجة الصوت إلى {level}%") | |
| except: pass | |
| # ======================================================= | |
| # التشغيل والـ Keep Alive | |
| # ======================================================= | |
| try: | |
| from keep_alive import keep_alive | |
| keep_alive() | |
| except Exception: pass | |
| if __name__ == "__main__": | |
| TOKEN = os.environ.get("DISCORD_TOKEN") | |
| if TOKEN: bot.run(TOKEN) | |
| else: print("❌ التوكن مفقود! يرجى التأكد من وضعه في المتغيرات البيئية (Secrets).") | |