Skydata001 commited on
Commit
b22819e
·
verified ·
1 Parent(s): af2f69d

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +30 -0
  2. bot.py +457 -0
  3. keep_alive.py +17 -0
  4. requirements.txt +5 -0
Dockerfile ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ # === تثبيت المتطلبات النظامية مع caching ===
4
+ RUN apt-get update && \
5
+ apt-get install -y --no-install-recommends \
6
+ ffmpeg gcc libffi-dev python3-dev curl && \
7
+ apt-get clean && \
8
+ rm -rf /var/lib/apt/lists/*
9
+
10
+ # === مستخدم آمن ===
11
+ RUN useradd -m -u 1000 user
12
+
13
+ # === مجلد البيانات ===
14
+ RUN mkdir -p /data && chown -R 1000:1000 /data
15
+
16
+ WORKDIR /app
17
+
18
+ # === تثبيت المكتبات مع caching أفضل (مهم جداً) ===
19
+ COPY --chown=user requirements.txt .
20
+ RUN pip install --no-cache-dir --upgrade pip && \
21
+ pip install --no-cache-dir -r requirements.txt
22
+
23
+ # === نسخ الكود ===
24
+ COPY --chown=user . .
25
+
26
+ USER user
27
+
28
+ EXPOSE 7860
29
+
30
+ CMD ["python", "bot.py"]
bot.py ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from discord.ui import Button, View, Select
5
+ import asyncio
6
+ import random
7
+ import os
8
+ from datetime import datetime
9
+
10
+ # --- الإعدادات الأساسية (الثوابت) ---
11
+ TARGET_GUILD_ID = 1463693244897693779 # أيدي السيرفر المسموح
12
+ OWNER_ROLE_ID = 1492516779598155877 # أيدي رتبة المالك للتحكم
13
+
14
+ # --- الأذكار والتذكيرات (القائمة الكاملة بدون اختصار) ---
15
+ REMINDERS = [
16
+ "🌸 سبحان الله وبحمده، سبحان الله العظيم",
17
+ "✨ اللهم صل وسلم على نبينا محمد",
18
+ "📿 أستغفر الله العظيم وأتوب إليه",
19
+ "🕊️ لا حول ولا قوة إلا بالله",
20
+ "🌿 سبحان الله، والحمد لله، ولا إله إلا الله، والله أكبر",
21
+ "🌸 سبحان الله",
22
+ "✨ الحمد لله",
23
+ "📿 الله أكبر",
24
+ "🕊️ لا إله إلا الله",
25
+ "🌿 اللهم اغفر لي",
26
+ "🌸 اللهم ارحمني",
27
+ "✨ اللهم تب علي",
28
+ "📿 اللهم ارزقني",
29
+ "🕊️ حسبي الله ونعم الوكيل",
30
+ "🌿 توكلت على الله",
31
+ "🌸 رب اغفر لي ولوالدي",
32
+ "✨ اللهم اجعلني من الصالحين",
33
+ "📿 اللهم اهدني",
34
+ "🕊️ اللهم ثبتني",
35
+ "🌿 اللهم ارزقني الجنة",
36
+ "🌸 اللهم أجرني من النار",
37
+ "✨ يا رب",
38
+ "📿 يا الله",
39
+ "🕊️ يا حي يا قيوم برحمتك أستغيث",
40
+ "🌿 اللهم إني أسألك العافية",
41
+ "🌸 اللهم إني أسألك الهدى والتقى",
42
+ "✨ اللهم إني أعوذ بك من الهم والحزن",
43
+ "📿 اللهم إني أعوذ بك من العجز والكسل",
44
+ "🕊️ اللهم إني أعوذ بك من الجبن والبخل",
45
+ "🌿 اللهم إني أعوذ بك من غلبة الدين وقهر الرجال",
46
+ "🌸 اللهم اجعل القرآن ربيع قلبي",
47
+ "✨ اللهم نور قلبي",
48
+ "📿 اللهم طهر قلبي",
49
+ "🕊️ اللهم أصلح حالي",
50
+ "🌿 اللهم اجعلني من الذاكرين",
51
+ "🌸 اللهم اجعلني من الشاكرين",
52
+ "✨ اللهم اجعلني من الصابرين",
53
+ "📿 اللهم اجعلني من التوابين",
54
+ "🕊️ اللهم اجعلني من المتقين",
55
+ "🌿 اللهم اجعلني من المحسنين",
56
+ "🌸 اللهم ارزقني الإخلاص",
57
+ "✨ اللهم ارزقني الصدق",
58
+ "📿 اللهم ارزقني حسن الخاتمة",
59
+ "🕊️ اللهم اجعل قبري روضة من رياض الجنة",
60
+ "🌿 اللهم اجعلني من أهل الجنة",
61
+ "🌸 اللهم ارزقني الفردوس الأعلى",
62
+ "✨ اللهم لا تحرمني من رحمتك",
63
+ "📿 اللهم لا تكلني إلى نفسي",
64
+ "🕊️ اللهم ارحم موتانا",
65
+ "🌿 اللهم اشف مرضانا",
66
+ "🌸 اللهم فرج همومنا",
67
+ "✨ اللهم اقض ديوننا",
68
+ "📿 اللهم وسع رزقنا",
69
+ "🕊️ اللهم بارك لنا",
70
+ "🌿 اللهم احفظنا",
71
+ "🌸 اللهم انصرنا",
72
+ "✨ اللهم اجعلنا من عبادك الصالحين",
73
+ "📿 اللهم ارزقنا رضاك",
74
+ "🕊️ اللهم اجعلنا من أهل الذكر",
75
+ "🌿 اللهم ارزقنا حبك",
76
+ "🌸 اللهم ارزقنا حب نبيك",
77
+ "✨ اللهم اجعلنا من المتوكلين",
78
+ "📿 اللهم ارزقنا الصبر",
79
+ "🕊️ اللهم ارزقنا اليقين",
80
+ "🌿 اللهم اجعلنا من الذاكرين الله كثيرا",
81
+ "🌸 اللهم اجعلنا من القانتين",
82
+ "✨ اللهم ارزقنا حسن الظن بك",
83
+ "📿 اللهم اجعلنا من المقبولين",
84
+ "🕊️ اللهم ارزقنا لذة النظر إلى وجهك",
85
+ "🌿 اللهم اجعلنا من الفائزين",
86
+ "🌸 اللهم اجعلنا من أهل الفردوس",
87
+ "✨ اللهم اجعلنا من عتقائك من النار",
88
+ "📿 اللهم اجعلنا من المستغفرين",
89
+ "🕊️ اللهم اجعلنا من الراجعين إليك",
90
+ "🌿 اللهم اجعلنا من المنيبين",
91
+ "🌸 اللهم ارزقنا التوبة النصوح",
92
+ "✨ اللهم تقبل منا",
93
+ "📿 اللهم اغفر لنا",
94
+ "🕊️ اللهم ارحمنا",
95
+ "🌿 اللهم تب علينا",
96
+ "🌸 اللهم اهدنا",
97
+ "✨ اللهم احفظنا من كل سوء",
98
+ "📿 اللهم اجعلنا من الصالحين",
99
+ "🕊️ اللهم ارزقنا الجنة بغير حساب",
100
+ "🌿 ا��لهم اجعلنا من أهل القرآن",
101
+ "🌸 اللهم اجعل القرآن شفيعاً لنا",
102
+ "✨ اللهم اجعل القرآن حجة لنا",
103
+ "📿 اللهم اجعلنا من الذين يستمعون القول فيتبعون أحسنه",
104
+ "🕊️ اللهم اجعلنا من أولي الألباب",
105
+ "🌿 اللهم ارزقنا العلم النافع",
106
+ "🌸 اللهم ارزقنا العمل الصالح",
107
+ "✨ اللهم ارزقنا الإيمان الكامل",
108
+ "📿 اللهم اجعلنا من أهل التقوى",
109
+ "🕊️ اللهم اجعلنا من الصادقين",
110
+ "🌿 اللهم اجعلنا من المخلصين",
111
+ "🌸 اللهم اجعلنا من عبادك المقربين",
112
+ "✨ اللهم اجعلنا من الذاكرين الشاكرين",
113
+ "📿 اللهم اجعلنا من الصابرين المحتسبين"
114
+ ]
115
+
116
+ # --- قائمة 30 قارئ (بدون اختصار) ---
117
+ RECITERS = [
118
+ {"name": "إذاعة تلاوات منوعة", "desc": "بث مباشر لتلاوات مختارة", "url": "https://qurango.net/radio/tarateel"},
119
+ {"name": "عبدالباسط عبدالصمد", "desc": "تلاوة مجودة بصوت خاشع", "url": "https://qurango.net/radio/abdulbasit_abdulsamad_mjawwad"},
120
+ {"name": "مشاري العفاسي", "desc": "تلاوة هادئة ومريحة", "url": "https://qurango.net/radio/mishary_alafasi"},
121
+ {"name": "ياسر الدوسري", "desc": "تلاوات مميزة من الحرم", "url": "https://qurango.net/radio/yasser_aldosari"},
122
+ {"name": "ماهر المعيقلي", "desc": "تلاوة عذبة وشهيرة", "url": "https://qurango.net/radio/maher_al_muaiqly"},
123
+ {"name": "أحمد بن علي العجمي", "desc": "تلاوة خاشعة", "url": "https://qurango.net/radio/ahmad_alajmy"},
124
+ {"name": "سعد الغامدي", "desc": "تلاوة مريحة للقلب", "url": "https://qurango.net/radio/saad_alghamidi"},
125
+ {"name": "سعود الشريم", "desc": "من تلاوات الحرم المكي", "url": "https://qurango.net/radio/saud_alshuraim"},
126
+ {"name": "عبدالرحمن السديس", "desc": "تلاوة الحرم المكي الشريف", "url": "https://qurango.net/radio/abdulrahman_alsudaes"},
127
+ {"name": "محمود خليل الحصري", "desc": "تلاوة مرتلة دقيقة", "url": "https://qurango.net/radio/mahmoud_khalil_alhussary"},
128
+ {"name": "علي عبدالله جابر", "desc": "تلاوة مميزة", "url": "https://qurango.net/radio/ali_jaber"},
129
+ {"name": "محمد أيوب", "desc": "تلاوة حجازية", "url": "https://qurango.net/radio/mohammed_ayyub"},
130
+ {"name": "محمد صديق المنشاوي", "desc": "تلاوة خاشعة جداً", "url": "https://qurango.net/radio/mohammed_siddiq_alminshawi"},
131
+ {"name": "أبوبكر الشاطري", "desc": "تلاوة هادئة", "url": "https://qurango.net/radio/abubakr_alshatri"},
132
+ {"name": "عبدالله بصفر", "desc": "تلاوة عذبة", "url": "https://qurango.net/radio/abdullah_basfer"},
133
+ {"name": "عبدالمحسن القاسم", "desc": "إمام المسجد النبوي", "url": "https://qurango.net/radio/abdulmohsin_alqasim"},
134
+ {"name": "عبدالودود حنيف", "desc": "تلاوة رائعة", "url": "https://qurango.net/radio/abdulwadood_haneef"},
135
+ {"name": "علي عبدالرحمن الحذيفي", "desc": "من تلاوات المسجد النبوي", "url": "https://qurango.net/radio/ali_alhuthaifi"},
136
+ {"name": "فارس عباد", "desc": "تلاوة مؤثرة", "url": "https://qurango.net/radio/fares_abbad"},
137
+ {"name": "محمد جبريل", "desc": "تلاوة مشهورة", "url": "https://qurango.net/radio/mohammed_jibreel"},
138
+ {"name": "محمد الطبلاوي", "desc": "من كبار القراء", "url": "https://qurango.net/radio/mohammad_altablaway"},
139
+ {"name": "محمود علي البنا", "desc": "تلاوة كلاسيكية", "url": "https://qurango.net/radio/mahmoud_ali__albanna"},
140
+ {"name": "مصطفى إسماعيل", "desc": "تلاوة مجودة", "url": "https://qurango.net/radio/mustafa_ismail"},
141
+ {"name": "هاني الرفاعي", "desc": "تلاوة بكائية", "url": "https://qurango.net/radio/hani_arrifai"},
142
+ {"name": "يحيى حوا", "desc": "تلاوة هادئة", "url": "https://qurango.net/radio/yahya_hawwa"},
143
+ {"name": "خالد القحطاني", "desc": "تلاوة خاشعة", "url": "https://qurango.net/radio/khalid_alqahtani"},
144
+ {"name": "صلاح البدير", "desc": "إمام المسجد النبوي", "url": "https://qurango.net/radio/salah_albudair"},
145
+ {"name": "صلاح بو خاطر", "desc": "تلاوة ندية", "url": "https://qurango.net/radio/salah_bukhatir"},
146
+ {"name": "عبدالرشيد صوفي", "desc": "تلاوة مميزة بروايات", "url": "https://qurango.net/radio/abdulrasheed_soufi"},
147
+ {"name": "عبدالله عواد الجهني", "desc": "إمام الحرم المكي", "url": "https://qurango.net/radio/abdullah_aljuhani"}
148
+ ]
149
+
150
+ # --- متغيرات التحكم بالبث ---
151
+ current_reciter_index = 0
152
+ current_volume = 1.0
153
+
154
+ FFMPEG_OPTIONS = {
155
+ 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
156
+ 'options': '-vn'
157
+ }
158
+
159
+ # =======================================================
160
+ # تجهيز واجهة الأزرار (Control Panel)
161
+ # =======================================================
162
+ class ReciterSelect(Select):
163
+ def __init__(self):
164
+ # القائمة المنسدلة في ديسكورد تدعم 25 خيار كحد أقصى
165
+ options = []
166
+ for i, r in enumerate(RECITERS[:25]):
167
+ options.append(discord.SelectOption(label=r['name'], description=r['desc'][:50], value=str(i)))
168
+ super().__init__(placeholder="اختر القارئ من القائمة السريعة...", min_values=1, max_values=1, options=options, custom_id="select_reciter")
169
+
170
+ async def callback(self, interaction: discord.Interaction):
171
+ role = interaction.guild.get_role(OWNER_ROLE_ID)
172
+ if role not in interaction.user.roles:
173
+ return await interaction.response.send_message("❌ لا تملك صلاحية.", ephemeral=True)
174
+
175
+ global current_reciter_index
176
+ current_reciter_index = int(self.values[0])
177
+ vc = interaction.guild.voice_client
178
+ if vc: await play_current_reciter(vc)
179
+
180
+ await interaction.response.send_message(f"🎙️ تم التغيير إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True)
181
+ await log_event(interaction.guild, f"🎙️ **{interaction.user.name}** قام بتغيير القارئ إلى: {RECITERS[current_reciter_index]['name']}")
182
+
183
+ class ControlPanelView(View):
184
+ def __init__(self):
185
+ super().__init__(timeout=None)
186
+ self.add_item(ReciterSelect())
187
+
188
+ async def check_owner(self, interaction: discord.Interaction):
189
+ role = interaction.guild.get_role(OWNER_ROLE_ID)
190
+ if role and role in interaction.user.roles:
191
+ return True
192
+ await interaction.response.send_message("❌ عذراً، لا تملك الصلاحية (رتبة المالك المطلوبة).", ephemeral=True)
193
+ return False
194
+
195
+ @discord.ui.button(label="⏸️ / ▶️ إيقاف وتشغيل", style=discord.ButtonStyle.primary, custom_id="btn_toggle")
196
+ async def toggle_btn(self, interaction: discord.Interaction, button: Button):
197
+ if not await self.check_owner(interaction): return
198
+ vc = interaction.guild.voice_client
199
+ if vc:
200
+ if vc.is_paused():
201
+ vc.resume()
202
+ await interaction.response.send_message("▶️ تم استئناف البث.", ephemeral=True)
203
+ await log_event(interaction.guild, f"▶️ **{interaction.user.name}** قام باستئناف البث.")
204
+ elif vc.is_playing():
205
+ vc.pause()
206
+ await interaction.response.send_message("⏸️ تم إيقاف البث مؤقتاً.", ephemeral=True)
207
+ await log_event(interaction.guild, f"⏸️ **{interaction.user.name}** قام بإيقاف البث يدوياً.")
208
+ else:
209
+ await play_current_reciter(vc)
210
+ await interaction.response.send_message("▶️ تم تشغيل البث.", ephemeral=True)
211
+ else:
212
+ await interaction.response.send_message("❌ البوت غير متصل.", ephemeral=True)
213
+
214
+ @discord.ui.button(label="⏭️ التالي", style=discord.ButtonStyle.secondary, custom_id="btn_next")
215
+ async def next_btn(self, interaction: discord.Interaction, button: Button):
216
+ if not await self.check_owner(interaction): return
217
+ global current_reciter_index
218
+ current_reciter_index = (current_reciter_index + 1) % len(RECITERS)
219
+ vc = interaction.guild.voice_client
220
+ if vc: await play_current_reciter(vc)
221
+ await interaction.response.send_message(f"⏭️ تم الانتقال إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True)
222
+ await log_event(interaction.guild, f"⏭️ **{interaction.user.name}** انتقل للقارئ: {RECITERS[current_reciter_index]['name']}")
223
+
224
+ @discord.ui.button(label="⏮️ السابق", style=discord.ButtonStyle.secondary, custom_id="btn_prev")
225
+ async def prev_btn(self, interaction: discord.Interaction, button: Button):
226
+ if not await self.check_owner(interaction): return
227
+ global current_reciter_index
228
+ current_reciter_index = (current_reciter_index - 1) % len(RECITERS)
229
+ vc = interaction.guild.voice_client
230
+ if vc: await play_current_reciter(vc)
231
+ await interaction.response.send_message(f"⏮️ تم الرجوع إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True)
232
+ await log_event(interaction.guild, f"⏮️ **{interaction.user.name}** رجع للقارئ: {RECITERS[current_reciter_index]['name']}")
233
+
234
+
235
+ class QuranBot(commands.Bot):
236
+ def __init__(self):
237
+ intents = discord.Intents.default()
238
+ intents.message_content = True
239
+ intents.voice_states = True
240
+ intents.guilds = True
241
+ super().__init__(command_prefix="!", intents=intents)
242
+
243
+ async def setup_hook(self):
244
+ # تسجيل لوحة التحكم لتعمل الأزرار حتى بعد إعادة تشغيل البوت
245
+ self.add_view(ControlPanelView())
246
+ await self.tree.sync()
247
+ print("✅ تم مزامنة الأوامر والأزرار بنجاح.")
248
+
249
+ bot = QuranBot()
250
+
251
+ # =======================================================
252
+ # دوال مساعدة والحماية
253
+ # =======================================================
254
+
255
+ # دالة لتسجيل الأحداث (Logs) في القناة المخصصة
256
+ async def log_event(guild: discord.Guild, message: str):
257
+ log_channel = discord.utils.get(guild.text_channels, name="📜-سجلات-البوت")
258
+ time_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
259
+ if log_channel:
260
+ try:
261
+ await log_channel.send(f"`[{time_now}]` {message}")
262
+ except: pass
263
+ print(f"[{time_now}] {message}")
264
+
265
+ # دالة للتحقق من الرتبة وأن الأمر يعمل في غرفة التحكم فقط
266
+ def is_owner_and_in_control():
267
+ async def predicate(interaction: discord.Interaction):
268
+ if not interaction.guild:
269
+ await interaction.response.send_message("❌ هذا الأمر مخصص للاستخدام داخل السيرفر فقط.", ephemeral=True)
270
+ return False
271
+
272
+ if interaction.channel.name != "🛠️ غرفة التحكم (للمالك)":
273
+ await interaction.response.send_message("❌ عذراً، لا يمكنك استخدام الأوامر إلا داخل 🛠️ غرفة التحكم.", ephemeral=True)
274
+ return False
275
+
276
+ role = interaction.guild.get_role(OWNER_ROLE_ID)
277
+ if role and role in interaction.user.roles:
278
+ return True
279
+
280
+ await interaction.response.send_message("❌ عذراً، لا تملك الصلاحية (رتبة المالك المطلوبة).", ephemeral=True)
281
+ return False
282
+ return app_commands.check(predicate)
283
+
284
+ async def play_current_reciter(vc: discord.VoiceClient):
285
+ if vc.is_playing() or vc.is_paused():
286
+ vc.stop()
287
+ reciter = RECITERS[current_reciter_index]
288
+ audio_source = discord.FFmpegPCMAudio(reciter["url"], **FFMPEG_OPTIONS)
289
+ vc.play(discord.PCMVolumeTransformer(audio_source, volume=current_volume))
290
+
291
+
292
+ # =======================================================
293
+ # هندسة القنوات والأحداث
294
+ # =======================================================
295
+
296
+ @bot.event
297
+ async def on_ready():
298
+ print(f"🤖 البوت {bot.user} يعمل الآن.")
299
+ for guild in bot.guilds:
300
+ if guild.id != TARGET_GUILD_ID:
301
+ print(f"👢 تم مغادرة سيرفر غريب: {guild.name}")
302
+ await guild.leave()
303
+
304
+ target_guild = bot.get_guild(TARGET_GUILD_ID)
305
+ if target_guild:
306
+ await check_and_create_channels(target_guild)
307
+ await log_event(target_guild, "🚀 **تم تشغيل البوت وهو جاهز للعمل.**")
308
+
309
+ async def check_and_create_channels(guild):
310
+ category_name = "إذاعة القرآن الكريم"
311
+ broadcast_vc_name = "🎧 استماع القرآن (بث عام)"
312
+ control_vc_name = "🛠️ غرفة التحكم (للمالك)"
313
+ log_channel_name = "📜-سجلات-البوت"
314
+
315
+ category = discord.utils.get(guild.categories, name=category_name)
316
+ broadcast_vc = discord.utils.get(guild.voice_channels, name=broadcast_vc_name)
317
+ control_vc = discord.utils.get(guild.voice_channels, name=control_vc_name)
318
+ log_tc = discord.utils.get(guild.text_channels, name=log_channel_name)
319
+
320
+ owner_role = guild.get_role(OWNER_ROLE_ID)
321
+
322
+ if not category:
323
+ category = await guild.create_category(category_name)
324
+
325
+ # 1. إنشاء قناة السجلات (Logs)
326
+ if not log_tc:
327
+ overwrites_log = {
328
+ guild.default_role: discord.PermissionOverwrite(view_channel=False),
329
+ bot.user: discord.PermissionOverwrite(view_channel=True, send_messages=True, read_message_history=True)
330
+ }
331
+ if owner_role: overwrites_log[owner_role] = discord.PermissionOverwrite(view_channel=True, read_message_history=True)
332
+ log_tc = await guild.create_text_channel(log_channel_name, category=category, overwrites=overwrites_log)
333
+
334
+ # 2. إنشاء قناة البث العام (منع مطلق للكتابة والتحدث للعامة)
335
+ if not broadcast_vc:
336
+ overwrites_broadcast = {
337
+ guild.default_role: discord.PermissionOverwrite(
338
+ view_channel=True, connect=True, read_message_history=True,
339
+ speak=False, send_messages=False, add_reactions=False, stream=False
340
+ ),
341
+ bot.user: discord.PermissionOverwrite(connect=True, speak=True, send_messages=True, administrator=True)
342
+ }
343
+ broadcast_vc = await guild.create_voice_channel(broadcast_vc_name, category=category, overwrites=overwrites_broadcast)
344
+
345
+ # 3. إنشاء غرفة التحكم للمالك
346
+ if not control_vc:
347
+ overwrites_control = {
348
+ guild.default_role: discord.PermissionOverwrite(view_channel=False, connect=False),
349
+ bot.user: discord.PermissionOverwrite(view_channel=True, connect=True, send_messages=True)
350
+ }
351
+ if owner_role: overwrites_control[owner_role] = discord.PermissionOverwrite(view_channel=True, connect=True, send_messages=True)
352
+ control_vc = await guild.create_voice_channel(control_vc_name, category=category, overwrites=overwrites_control, user_limit=99)
353
+
354
+ # إرسال لوحة التحكم (الرسالة التفاعلية) داخل شات غرفة التحكم
355
+ embed = discord.Embed(
356
+ title="🎛️ لوحة تحكم الإذاعة الذكية",
357
+ description="استخدم الأزرار أدناه للتحكم السريع في البث، أو اختر القارئ من القائمة المنسدلة.\n\n*ملاحظة: يمكنك أيضاً استخدام الأوامر مثل `/volume` هنا فقط.*",
358
+ color=discord.Color.dark_theme()
359
+ )
360
+ embed.set_footer(text="SaaS Pro Dashboard")
361
+ await control_vc.send(embed=embed, view=ControlPanelView())
362
+
363
+ # اتصال البوت التلقائي بقناة البث العام
364
+ if not guild.voice_client:
365
+ vc = await broadcast_vc.connect()
366
+ await play_current_reciter(vc)
367
+
368
+ human_members = sum(1 for m in broadcast_vc.members if not m.bot)
369
+ if human_members == 0:
370
+ vc.pause()
371
+ await log_event(guild, "⏸️ القناة فارغة، تم وضع البث في وضع الاستعداد (توفير الموارد).")
372
+ else:
373
+ await log_event(guild, "▶️ تم البدء بالبث لوجود مستمعين في القناة.")
374
+
375
+ @bot.event
376
+ async def on_voice_state_update(member, before, after):
377
+ if member.bot: return
378
+ guild = member.guild
379
+ if guild.id != TARGET_GUILD_ID: return
380
+ bot_vc = guild.voice_client
381
+ if not bot_vc: return
382
+
383
+ # حالة دخول عضو
384
+ if after.channel and after.channel.id == bot_vc.channel.id:
385
+ if bot_vc.is_paused():
386
+ bot_vc.resume()
387
+ await log_event(guild, f"▶️ استئناف البث التلقائي بسبب دخول **{member.name}**.")
388
+
389
+ embed = discord.Embed(
390
+ title="أهلاً بك في مجلس الذكر 🕊️",
391
+ description=random.choice(REMINDERS),
392
+ color=discord.Color.gold()
393
+ )
394
+ embed.set_footer(text=f"القارئ الحالي: {RECITERS[current_reciter_index]['name']}")
395
+ try:
396
+ await after.channel.send(content=f"مرحباً بك {member.mention}", embed=embed, delete_after=60)
397
+ except: pass
398
+
399
+ # حالة خروج عضو
400
+ elif before.channel and before.channel.id == bot_vc.channel.id:
401
+ human_members = sum(1 for m in before.channel.members if not m.bot)
402
+ if human_members == 0 and bot_vc.is_playing():
403
+ bot_vc.pause()
404
+ await log_event(guild, "💤 توقف البث تلقائياً لخروج جميع المستمعين.")
405
+
406
+
407
+ # =======================================================
408
+ # أوامر السلاش (محصورة في غرفة التحكم)
409
+ # =======================================================
410
+
411
+ @bot.tree.command(name="play_radio", description="▶️ تشغيل بث القرآن إجبارياً")
412
+ @is_owner_and_in_control()
413
+ async def play_radio(interaction: discord.Interaction):
414
+ vc = interaction.guild.voice_client
415
+ if not vc: return await interaction.response.send_message("❌ البوت غير متصل.", ephemeral=True)
416
+ if vc.is_paused(): vc.resume()
417
+ else: await play_current_reciter(vc)
418
+ await interaction.response.send_message(f"▶️ تم التشغيل: **{RECITERS[current_reciter_index]['name']}**", ephemeral=True)
419
+ await log_event(interaction.guild, f"▶️ **{interaction.user.name}** شغل البث عبر أمر /play_radio")
420
+
421
+ @bot.tree.command(name="stop_radio", description="⏹️ إيقاف البث مؤقتاً")
422
+ @is_owner_and_in_control()
423
+ async def stop_radio(interaction: discord.Interaction):
424
+ vc = interaction.guild.voice_client
425
+ if vc and vc.is_playing():
426
+ vc.pause()
427
+ await interaction.response.send_message("⏹️ تم الإيقاف.", ephemeral=True)
428
+ await log_event(interaction.guild, f"⏹️ **{interaction.user.name}** أوقف البث عبر أمر /stop_radio")
429
+ else:
430
+ await interaction.response.send_message("⚠️ متوقف بالفعل.", ephemeral=True)
431
+
432
+ @bot.tree.command(name="volume", description="🔊 التحكم بدرجة الصوت (من 1 إلى 100)")
433
+ @app_commands.describe(level="درجة الصوت من 1 إلى 100")
434
+ @is_owner_and_in_control()
435
+ async def set_volume(interaction: discord.Interaction, level: int):
436
+ global current_volume
437
+ if level < 1 or level > 100:
438
+ return await interaction.response.send_message("❌ يرجى إدخال رقم بين 1 و 100.", ephemeral=True)
439
+
440
+ current_volume = level / 100.0
441
+ vc = interaction.guild.voice_client
442
+ if vc and vc.source: vc.source.volume = current_volume
443
+
444
+ await interaction.response.send_message(f"🔊 تم تغيير درجة الصوت إلى: **{level}%**", ephemeral=True)
445
+ await log_event(interaction.guild, f"🔊 **{interaction.user.name}** غيّر درجة الصوت إلى {level}%")
446
+
447
+ # =======================================================
448
+ # التشغيل والـ Keep Alive
449
+ # =======================================================
450
+ try:
451
+ from keep_alive import keep_alive
452
+ keep_alive()
453
+ except Exception: pass
454
+
455
+ if __name__ == "__main__":
456
+ TOKEN = os.environ.get("DISCORD_TOKEN")
457
+ if TOKEN: bot.run(TOKEN)
keep_alive.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask
2
+ from threading import Thread
3
+
4
+ app = Flask(__name__)
5
+
6
+ @app.route('/')
7
+ def home():
8
+ # هذه الرسالة البسيطة تكفي لكي يعرف UptimeRobot أن البوت حي
9
+ return "🟢 البوت يعمل بنجاح! جاهز للبث 24/7"
10
+
11
+ def run():
12
+ # المنفذ 7860 هو المنفذ المعتمد في Hugging Face Spaces
13
+ app.run(host='0.0.0.0', port=7860)
14
+
15
+ def keep_alive():
16
+ t = Thread(target=run)
17
+ t.start()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ discord.py[voice]
2
+ PyNaCl
3
+ flask
4
+ supabase
5
+ google-generativeai