Skydata001 commited on
Commit
4340a48
·
verified ·
1 Parent(s): 28520ff

Update bot.py

Browse files
Files changed (1) hide show
  1. bot.py +224 -233
bot.py CHANGED
@@ -1,5 +1,5 @@
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
@@ -22,99 +22,44 @@ REMINDERS = [
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
- "✨ اللهم اجعلنا من الذاكرين الشاكرين",
117
- "📿 اللهم اجعلنا من الصابرين المحتسبين"
118
  ]
119
 
120
  # =======================================================
@@ -157,9 +102,10 @@ RECITERS = [
157
  current_reciter_index = 0
158
  current_volume = 1.0
159
 
 
160
  FFMPEG_OPTIONS = {
161
  'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
162
- 'options': '-vn'
163
  }
164
 
165
  # =======================================================
@@ -167,7 +113,6 @@ FFMPEG_OPTIONS = {
167
  # =======================================================
168
  class ReciterSelect(Select):
169
  def __init__(self):
170
- # القائمة المنسدلة في ديسكورد تدعم 25 خيار كحد أقصى
171
  options = []
172
  for i, r in enumerate(RECITERS[:25]):
173
  options.append(discord.SelectOption(label=r['name'], description=r['desc'][:50], value=str(i)))
@@ -183,8 +128,10 @@ class ReciterSelect(Select):
183
  vc = interaction.guild.voice_client
184
  if vc: await play_current_reciter(vc)
185
 
186
- await interaction.response.send_message(f"🎙️ تم التغيير إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True)
187
- await log_event(interaction.guild, f"🎙️ **{interaction.user.name}** قام بتغيير القارئ إلى: {RECITERS[current_reciter_index]['name']}")
 
 
188
 
189
  class ControlPanelView(View):
190
  def __init__(self):
@@ -195,27 +142,31 @@ class ControlPanelView(View):
195
  role = interaction.guild.get_role(OWNER_ROLE_ID)
196
  if role and role in interaction.user.roles:
197
  return True
198
- await interaction.response.send_message("❌ عذراً، لا تملك الصلاحية (رتبة المالك المطلوبة).", ephemeral=True)
 
 
199
  return False
200
 
201
  @discord.ui.button(label="⏸️ / ▶️ إيقاف وتشغيل", style=discord.ButtonStyle.primary, custom_id="btn_toggle")
202
  async def toggle_btn(self, interaction: discord.Interaction, button: Button):
203
  if not await self.check_owner(interaction): return
204
  vc = interaction.guild.voice_client
205
- if vc:
206
- if vc.is_paused():
207
- vc.resume()
208
- await interaction.response.send_message("▶️ تم استئناف البث.", ephemeral=True)
209
- await log_event(interaction.guild, f"▶️ **{interaction.user.name}** قام باستئناف البث.")
210
- elif vc.is_playing():
211
- vc.pause()
212
- await interaction.response.send_message("⏸️ تم إيقاف البث مؤقتاً.", ephemeral=True)
213
- await log_event(interaction.guild, f"⏸️ **{interaction.user.name}** قام بإيقاف البث يدوياً.")
 
 
 
 
214
  else:
215
- await play_current_reciter(vc)
216
- await interaction.response.send_message("▶️ تم تشغيل البث.", ephemeral=True)
217
- else:
218
- await interaction.response.send_message("❌ البوت غير متصل.", ephemeral=True)
219
 
220
  @discord.ui.button(label="⏭️ التالي", style=discord.ButtonStyle.secondary, custom_id="btn_next")
221
  async def next_btn(self, interaction: discord.Interaction, button: Button):
@@ -224,8 +175,10 @@ class ControlPanelView(View):
224
  current_reciter_index = (current_reciter_index + 1) % len(RECITERS)
225
  vc = interaction.guild.voice_client
226
  if vc: await play_current_reciter(vc)
227
- await interaction.response.send_message(f"⏭️ تم الانتقال إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True)
228
- await log_event(interaction.guild, f"⏭️ **{interaction.user.name}** انتقل للقارئ: {RECITERS[current_reciter_index]['name']}")
 
 
229
 
230
  @discord.ui.button(label="⏮️ السابق", style=discord.ButtonStyle.secondary, custom_id="btn_prev")
231
  async def prev_btn(self, interaction: discord.Interaction, button: Button):
@@ -234,8 +187,10 @@ class ControlPanelView(View):
234
  current_reciter_index = (current_reciter_index - 1) % len(RECITERS)
235
  vc = interaction.guild.voice_client
236
  if vc: await play_current_reciter(vc)
237
- await interaction.response.send_message(f"⏮️ تم الرجوع إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True)
238
- await log_event(interaction.guild, f"⏮️ **{interaction.user.name}** رجع للقارئ: {RECITERS[current_reciter_index]['name']}")
 
 
239
 
240
  class QuranBot(commands.Bot):
241
  def __init__(self):
@@ -246,7 +201,6 @@ class QuranBot(commands.Bot):
246
  super().__init__(command_prefix="!", intents=intents)
247
 
248
  async def setup_hook(self):
249
- # تسجيل لوحة التحكم لتعمل الأزرار حتى بعد إعادة تشغيل البوت
250
  self.add_view(ControlPanelView())
251
  await self.tree.sync()
252
  print("✅ تم مزامنة الأوامر والأزرار بنجاح.")
@@ -254,7 +208,7 @@ class QuranBot(commands.Bot):
254
  bot = QuranBot()
255
 
256
  # =======================================================
257
- # دوال مساعدة والحماية
258
  # =======================================================
259
 
260
  async def log_event(guild: discord.Guild, message: str):
@@ -263,34 +217,41 @@ async def log_event(guild: discord.Guild, message: str):
263
  if log_channel:
264
  try:
265
  await log_channel.send(f"`[{time_now}]` {message}")
266
- except: pass
267
  print(f"[{time_now}] {message}")
268
 
269
  def is_owner_and_in_control():
270
  async def predicate(interaction: discord.Interaction):
271
  if not interaction.guild:
272
- await interaction.response.send_message("❌ هذا الأمر مخصص للاستخدام داخل السيرفر فقط.", ephemeral=True)
 
273
  return False
274
 
275
  if interaction.channel.name != "🛠️ غرفة التحكم (للمالك)":
276
- await interaction.response.send_message("❌ عذراً، لا يمكنك استخدام الأوامر إلا داخل 🛠️ غرفة التحكم.", ephemeral=True)
 
277
  return False
278
 
279
  role = interaction.guild.get_role(OWNER_ROLE_ID)
280
  if role and role in interaction.user.roles:
281
  return True
282
 
283
- await interaction.response.send_message("❌ عذراً، لا تملك الصلاحية (رتبة المالك المطلوبة).", ephemeral=True)
 
284
  return False
285
  return app_commands.check(predicate)
286
 
287
  async def play_current_reciter(vc: discord.VoiceClient):
288
- if vc.is_playing() or vc.is_paused():
289
- vc.stop()
290
- reciter = RECITERS[current_reciter_index]
291
- audio_source = discord.FFmpegPCMAudio(reciter["url"], **FFMPEG_OPTIONS)
292
- vc.play(discord.PCMVolumeTransformer(audio_source, volume=current_volume))
293
-
 
 
 
 
294
 
295
  # =======================================================
296
  # هندسة القنوات والأحداث
@@ -298,32 +259,36 @@ async def play_current_reciter(vc: discord.VoiceClient):
298
 
299
  @bot.event
300
  async def on_ready():
301
- # 1. جعل البوت يظهر كـ "متصل" بشكل دائم مع حالة (Watching...)
302
- await bot.change_presence(status=discord.Status.online, activity=discord.Activity(type=discord.ActivityType.watching, name="جاهز للعمل وينتظر السيرفر..."))
303
  print(f"🤖 البوت {bot.user} متصل بشبكة ديسكورد بنجاح وهو الآن (Online).")
304
 
305
- # 2. فحص السيرفرات التي يتواجد فيها حالياً عند التشغيل
306
  for guild in bot.guilds:
307
  if guild.id != TARGET_GUILD_ID:
308
- print(f"👢 تم مغادرة سيرفر غريب: {guild.name}")
309
- await guild.leave()
 
 
310
  else:
311
- # إذا وجد السيرفر الصحيح، يبدأ بتجهيز القنوات
312
  await check_and_create_channels(guild)
313
- await log_event(guild, "🚀 **تم إعادة تشغيل البوت وهو متصل الآن.**")
 
 
 
 
314
 
315
  @bot.event
316
  async def on_guild_join(guild):
317
- # 3. هذا الحدث يعمل "فوراً" بمجرد إدخال البوت لأي سيرفر جديد
318
- print(f"📥 البوت دخل سيرفر جديد: {guild.name} | الأيدي: {guild.id}")
319
 
320
  if guild.id != TARGET_GUILD_ID:
321
- print("❌ هذا ليس السيرفر المصرح له، جاري المغادرة...")
322
- await guild.leave()
323
  else:
324
- print("✅ تم دخول السيرفر الصحيح! جاري تجهيز النظام...")
325
  await check_and_create_channels(guild)
326
- await log_event(guild, "🚀 **تم إضافة البوت للسيرفر بنجاح، القنوات جاهزة!**")
327
 
328
  async def check_and_create_channels(guild):
329
  category_name = "إذاعة القرآن الكريم"
@@ -338,58 +303,62 @@ async def check_and_create_channels(guild):
338
 
339
  owner_role = guild.get_role(OWNER_ROLE_ID)
340
 
341
- if not category:
342
- category = await guild.create_category(category_name)
343
-
344
- # 1. إنشاء قناة السجلات (Logs)
345
- if not log_tc:
346
- overwrites_log = {
347
- guild.default_role: discord.PermissionOverwrite(view_channel=False),
348
- bot.user: discord.PermissionOverwrite(view_channel=True, send_messages=True, read_message_history=True)
349
- }
350
- if owner_role: overwrites_log[owner_role] = discord.PermissionOverwrite(view_channel=True, read_message_history=True)
351
- log_tc = await guild.create_text_channel(log_channel_name, category=category, overwrites=overwrites_log)
352
-
353
- # 2. إنشاء قناة البث العام (منع مطلق للكتابة والتحدث للعامة)
354
- if not broadcast_vc:
355
- overwrites_broadcast = {
356
- guild.default_role: discord.PermissionOverwrite(
357
- view_channel=True, connect=True, read_message_history=True,
358
- speak=False, send_messages=False, add_reactions=False, stream=False
359
- ),
360
- bot.user: discord.PermissionOverwrite(connect=True, speak=True, send_messages=True, administrator=True)
361
- }
362
- broadcast_vc = await guild.create_voice_channel(broadcast_vc_name, category=category, overwrites=overwrites_broadcast)
363
-
364
- # 3. إنشاء غرفة التحكم للمالك
365
- if not control_vc:
366
- overwrites_control = {
367
- guild.default_role: discord.PermissionOverwrite(view_channel=False, connect=False),
368
- bot.user: discord.PermissionOverwrite(view_channel=True, connect=True, send_messages=True)
369
- }
370
- if owner_role: overwrites_control[owner_role] = discord.PermissionOverwrite(view_channel=True, connect=True, send_messages=True)
371
- control_vc = await guild.create_voice_channel(control_vc_name, category=category, overwrites=overwrites_control, user_limit=99)
372
-
373
- # إرسال لوحة التحكم (الرسالة التفاعلية) داخل شات غرفة التحكم
374
- embed = discord.Embed(
375
- title="🎛️ لوحة تحكم الإذاعة الذكية",
376
- description="استخدم الأزرار أدناه للتحكم السريع في البث، أو اختر القارئ من القائمة المنسدلة.\n\n*ملاحظة: يمكنك أيضاً استخدام الأوامر مثل `/volume` هنا فقط.*",
377
- color=discord.Color.dark_theme()
378
- )
379
- embed.set_footer(text="SaaS Pro Dashboard")
380
- await control_vc.send(embed=embed, view=ControlPanelView())
381
-
382
- # اتصال البوت التلقائي بقناة البث العام
383
- if not guild.voice_client:
384
- vc = await broadcast_vc.connect()
385
- await play_current_reciter(vc)
386
-
387
- human_members = sum(1 for m in broadcast_vc.members if not m.bot)
388
- if human_members == 0:
389
- vc.pause()
390
- await log_event(guild, "⏸️ القناة فارغة، تم وضع البث في وضع الاستعداد (توفير الموارد).")
391
- else:
392
- await log_event(guild, "▶️ تم البدء بالبث لوجود مستمعين في القناة.")
 
 
 
 
393
 
394
  @bot.event
395
  async def on_voice_state_update(member, before, after):
@@ -399,29 +368,45 @@ async def on_voice_state_update(member, before, after):
399
  bot_vc = guild.voice_client
400
  if not bot_vc: return
401
 
402
- # حالة دخول عضو
403
- if after.channel and after.channel.id == bot_vc.channel.id:
404
- if bot_vc.is_paused():
405
- bot_vc.resume()
406
- await log_event(guild, f"▶️ استئناف البث التلقائي بسبب دخول **{member.name}**.")
407
-
408
- embed = discord.Embed(
409
- title="أهلاً بك في مجلس الذكر 🕊️",
410
- description=random.choice(REMINDERS),
411
- color=discord.Color.gold()
412
- )
413
- embed.set_footer(text=f"القارئ الحالي: {RECITERS[current_reciter_index]['name']}")
414
- try:
415
- await after.channel.send(content=f"مرحباً بك {member.mention}", embed=embed, delete_after=60)
416
- except: pass
417
-
418
- # حالة خروج عضو
419
- elif before.channel and before.channel.id == bot_vc.channel.id:
420
- human_members = sum(1 for m in before.channel.members if not m.bot)
421
- if human_members == 0 and bot_vc.is_playing():
422
- bot_vc.pause()
423
- await log_event(guild, "💤 توقف البث تلقائياً لخروج جميع المستمعين.")
 
424
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
 
426
  # =======================================================
427
  # أوامر السلاش (محصورة في غرفة التحكم)
@@ -431,37 +416,43 @@ async def on_voice_state_update(member, before, after):
431
  @is_owner_and_in_control()
432
  async def play_radio(interaction: discord.Interaction):
433
  vc = interaction.guild.voice_client
434
- if not vc: return await interaction.response.send_message("❌ البوت غير متصل.", ephemeral=True)
435
- if vc.is_paused(): vc.resume()
436
- else: await play_current_reciter(vc)
437
- await interaction.response.send_message(f"▶️ تم التشغيل: **{RECITERS[current_reciter_index]['name']}**", ephemeral=True)
438
- await log_event(interaction.guild, f"▶️ **{interaction.user.name}** شغل البث عبر أمر /play_radio")
 
 
439
 
440
  @bot.tree.command(name="stop_radio", description="⏹️ إيقاف البث مؤقتاً")
441
  @is_owner_and_in_control()
442
  async def stop_radio(interaction: discord.Interaction):
443
  vc = interaction.guild.voice_client
444
- if vc and vc.is_playing():
445
- vc.pause()
446
- await interaction.response.send_message("⏹️ تم الإيقاف.", ephemeral=True)
447
- await log_event(interaction.guild, f"⏹️ **{interaction.user.name}** أوقف البث عبر أمر /stop_radio")
448
- else:
449
- await interaction.response.send_message("⚠️ متوقف بالفعل.", ephemeral=True)
 
 
450
 
451
  @bot.tree.command(name="volume", description="🔊 التحكم بدرجة الصوت (من 1 إلى 100)")
452
  @app_commands.describe(level="درجة الصوت من 1 إلى 100")
453
  @is_owner_and_in_control()
454
  async def set_volume(interaction: discord.Interaction, level: int):
455
  global current_volume
456
- if level < 1 or level > 100:
457
- return await interaction.response.send_message("❌ يرجى إدخال رقم بين 1 و 100.", ephemeral=True)
458
-
459
- current_volume = level / 100.0
460
- vc = interaction.guild.voice_client
461
- if vc and vc.source: vc.source.volume = current_volume
462
 
463
- await interaction.response.send_message(f"🔊 تم تغيير درجة الصوت إلى: **{level}%**", ephemeral=True)
464
- await log_event(interaction.guild, f"🔊 **{interaction.user.name}** غيّر درجة الصوت إلى {level}%")
 
 
 
 
 
465
 
466
  # =======================================================
467
  # التشغيل والـ Keep Alive
 
1
  import discord
2
+ from discord.ext import commands, tasks
3
  from discord import app_commands
4
  from discord.ui import Button, View, Select
5
  import asyncio
 
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
  # =======================================================
 
102
  current_reciter_index = 0
103
  current_volume = 1.0
104
 
105
+ # إعدادات أكثر أماناً لتجنب حظر IP من السيرفرات الصوتية
106
  FFMPEG_OPTIONS = {
107
  'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
108
+ 'options': '-vn -sn -dn -bufsize 500k -max_muxing_queue_size 1024'
109
  }
110
 
111
  # =======================================================
 
113
  # =======================================================
114
  class ReciterSelect(Select):
115
  def __init__(self):
 
116
  options = []
117
  for i, r in enumerate(RECITERS[:25]):
118
  options.append(discord.SelectOption(label=r['name'], description=r['desc'][:50], value=str(i)))
 
128
  vc = interaction.guild.voice_client
129
  if vc: await play_current_reciter(vc)
130
 
131
+ try:
132
+ await interaction.response.send_message(f"🎙️ تم التغيير إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True)
133
+ await log_event(interaction.guild, f"🎙️ **{interaction.user.name}** قام بتغيير القارئ إلى: {RECITERS[current_reciter_index]['name']}")
134
+ except: pass
135
 
136
  class ControlPanelView(View):
137
  def __init__(self):
 
142
  role = interaction.guild.get_role(OWNER_ROLE_ID)
143
  if role and role in interaction.user.roles:
144
  return True
145
+ try:
146
+ await interaction.response.send_message("❌ عذراً، لا تملك الصلاحية (رتبة المالك المطلوبة).", ephemeral=True)
147
+ except: pass
148
  return False
149
 
150
  @discord.ui.button(label="⏸️ / ▶️ إيقاف وتشغيل", style=discord.ButtonStyle.primary, custom_id="btn_toggle")
151
  async def toggle_btn(self, interaction: discord.Interaction, button: Button):
152
  if not await self.check_owner(interaction): return
153
  vc = interaction.guild.voice_client
154
+ try:
155
+ if vc:
156
+ if vc.is_paused():
157
+ vc.resume()
158
+ await interaction.response.send_message("▶️ تم استئناف البث.", ephemeral=True)
159
+ await log_event(interaction.guild, f"▶️ **{interaction.user.name}** قام باستئناف البث.")
160
+ elif vc.is_playing():
161
+ vc.pause()
162
+ await interaction.response.send_message("⏸️ تم إيقاف البث مؤقتاً.", ephemeral=True)
163
+ await log_event(interaction.guild, f"⏸️ **{interaction.user.name}** قام بإيقاف البث يدوياً.")
164
+ else:
165
+ await play_current_reciter(vc)
166
+ await interaction.response.send_message("▶️ تم تشغيل البث.", ephemeral=True)
167
  else:
168
+ await interaction.response.send_message("❌ البوت غير متصل.", ephemeral=True)
169
+ except: pass
 
 
170
 
171
  @discord.ui.button(label="⏭️ التالي", style=discord.ButtonStyle.secondary, custom_id="btn_next")
172
  async def next_btn(self, interaction: discord.Interaction, button: Button):
 
175
  current_reciter_index = (current_reciter_index + 1) % len(RECITERS)
176
  vc = interaction.guild.voice_client
177
  if vc: await play_current_reciter(vc)
178
+ try:
179
+ await interaction.response.send_message(f"⏭️ تم الانتقال إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True)
180
+ await log_event(interaction.guild, f"⏭️ **{interaction.user.name}** انتقل للقارئ: {RECITERS[current_reciter_index]['name']}")
181
+ except: pass
182
 
183
  @discord.ui.button(label="⏮️ السابق", style=discord.ButtonStyle.secondary, custom_id="btn_prev")
184
  async def prev_btn(self, interaction: discord.Interaction, button: Button):
 
187
  current_reciter_index = (current_reciter_index - 1) % len(RECITERS)
188
  vc = interaction.guild.voice_client
189
  if vc: await play_current_reciter(vc)
190
+ try:
191
+ await interaction.response.send_message(f"⏮️ تم الرجوع إلى: {RECITERS[current_reciter_index]['name']}", ephemeral=True)
192
+ await log_event(interaction.guild, f"⏮️ **{interaction.user.name}** رجع للقارئ: {RECITERS[current_reciter_index]['name']}")
193
+ except: pass
194
 
195
  class QuranBot(commands.Bot):
196
  def __init__(self):
 
201
  super().__init__(command_prefix="!", intents=intents)
202
 
203
  async def setup_hook(self):
 
204
  self.add_view(ControlPanelView())
205
  await self.tree.sync()
206
  print("✅ تم مزامنة الأوامر والأزرار بنجاح.")
 
208
  bot = QuranBot()
209
 
210
  # =======================================================
211
+ # دوال مساعدة وحماية من الحظر
212
  # =======================================================
213
 
214
  async def log_event(guild: discord.Guild, message: str):
 
217
  if log_channel:
218
  try:
219
  await log_channel.send(f"`[{time_now}]` {message}")
220
+ except: pass # منع توقف البوت إذا رفض ديسكورد إرسال الرسالة بسبب Rate Limit
221
  print(f"[{time_now}] {message}")
222
 
223
  def is_owner_and_in_control():
224
  async def predicate(interaction: discord.Interaction):
225
  if not interaction.guild:
226
+ try: await interaction.response.send_message("❌ هذا الأمر مخصص للاستخدام داخل السيرفر فقط.", ephemeral=True)
227
+ except: pass
228
  return False
229
 
230
  if interaction.channel.name != "🛠️ غرفة التحكم (للمالك)":
231
+ try: await interaction.response.send_message("❌ عذراً، لا يمكنك استخدام الأوامر إلا داخل 🛠️ غرفة التحكم.", ephemeral=True)
232
+ except: pass
233
  return False
234
 
235
  role = interaction.guild.get_role(OWNER_ROLE_ID)
236
  if role and role in interaction.user.roles:
237
  return True
238
 
239
+ try: await interaction.response.send_message("❌ عذراً، لا تملك الصلاحية (رتبة المالك المطلوبة).", ephemeral=True)
240
+ except: pass
241
  return False
242
  return app_commands.check(predicate)
243
 
244
  async def play_current_reciter(vc: discord.VoiceClient):
245
+ try:
246
+ if vc.is_playing() or vc.is_paused():
247
+ vc.stop()
248
+ await asyncio.sleep(1) # تأخير بسيط لحماية الـ API من ضغط الطلبات
249
+
250
+ reciter = RECITERS[current_reciter_index]
251
+ audio_source = discord.FFmpegPCMAudio(reciter["url"], **FFMPEG_OPTIONS)
252
+ vc.play(discord.PCMVolumeTransformer(audio_source, volume=current_volume))
253
+ except Exception as e:
254
+ print(f"⚠️ خطأ أثناء التشغيل (تم تجاوزه): {e}")
255
 
256
  # =======================================================
257
  # هندسة القنوات والأحداث
 
259
 
260
  @bot.event
261
  async def on_ready():
262
+ # حالة مستقرة وآمنة تظهر للمستخدمين
263
+ await bot.change_presence(status=discord.Status.online, activity=discord.Activity(type=discord.ActivityType.listening, name="القرآن الكريم 24/7 🎧"))
264
  print(f"🤖 البوت {bot.user} متصل بشبكة ديسكورد بنجاح وهو الآن (Online).")
265
 
266
+ # فحص هادئ للسيرفرات
267
  for guild in bot.guilds:
268
  if guild.id != TARGET_GUILD_ID:
269
+ print(f"👢 جاري مغادرة سيرفر غريب بهدوء: {guild.name}")
270
+ await asyncio.sleep(2) # تأخير لمنع الطرد المتعدد السريع
271
+ try: await guild.leave()
272
+ except: pass
273
  else:
 
274
  await check_and_create_channels(guild)
275
+ await log_event(guild, "🚀 **تم إقلاع النظام والبوت متصل الآن.**")
276
+
277
+ # تشغيل نبض القلب (فحص كل 5 دقائق)
278
+ if not heartbeat_task.is_running():
279
+ heartbeat_task.start()
280
 
281
  @bot.event
282
  async def on_guild_join(guild):
283
+ print(f"📥 البوت دخل سيرفر جديد: {guild.name}")
284
+ await asyncio.sleep(2) # حماية API ديسكورد
285
 
286
  if guild.id != TARGET_GUILD_ID:
287
+ try: await guild.leave()
288
+ except: pass
289
  else:
 
290
  await check_and_create_channels(guild)
291
+ await log_event(guild, "🚀 **تم إضافة البوت للسيرفر الصحيح وبدء التشغيل!**")
292
 
293
  async def check_and_create_channels(guild):
294
  category_name = "إذاعة القرآن الكريم"
 
303
 
304
  owner_role = guild.get_role(OWNER_ROLE_ID)
305
 
306
+ try:
307
+ if not category:
308
+ category = await guild.create_category(category_name)
309
+ await asyncio.sleep(1)
310
+
311
+ if not log_tc:
312
+ overwrites_log = {
313
+ guild.default_role: discord.PermissionOverwrite(view_channel=False),
314
+ bot.user: discord.PermissionOverwrite(view_channel=True, send_messages=True, read_message_history=True)
315
+ }
316
+ if owner_role: overwrites_log[owner_role] = discord.PermissionOverwrite(view_channel=True, read_message_history=True)
317
+ log_tc = await guild.create_text_channel(log_channel_name, category=category, overwrites=overwrites_log)
318
+ await asyncio.sleep(1)
319
+
320
+ if not broadcast_vc:
321
+ overwrites_broadcast = {
322
+ guild.default_role: discord.PermissionOverwrite(
323
+ view_channel=True, connect=True, read_message_history=True,
324
+ speak=False, send_messages=False, add_reactions=False, stream=False
325
+ ),
326
+ bot.user: discord.PermissionOverwrite(connect=True, speak=True, send_messages=True, administrator=True)
327
+ }
328
+ broadcast_vc = await guild.create_voice_channel(broadcast_vc_name, category=category, overwrites=overwrites_broadcast)
329
+ await asyncio.sleep(1)
330
+
331
+ if not control_vc:
332
+ overwrites_control = {
333
+ guild.default_role: discord.PermissionOverwrite(view_channel=False, connect=False),
334
+ bot.user: discord.PermissionOverwrite(view_channel=True, connect=True, send_messages=True)
335
+ }
336
+ if owner_role: overwrites_control[owner_role] = discord.PermissionOverwrite(view_channel=True, connect=True, send_messages=True)
337
+ control_vc = await guild.create_voice_channel(control_vc_name, category=category, overwrites=overwrites_control, user_limit=99)
338
+
339
+ embed = discord.Embed(
340
+ title="🎛️ لوحة تحكم الإذاعة الذكية",
341
+ description="استخدم الأزرار أدناه للتحكم السريع في البث، أو اختر القارئ من القائمة المنسدلة.\n\n*ملاحظة: يمكنك أيضاً استخدام الأوامر مثل `/volume` هنا فقط.*",
342
+ color=discord.Color.dark_theme()
343
+ )
344
+ embed.set_footer(text="SaaS Pro Dashboard")
345
+ await control_vc.send(embed=embed, view=ControlPanelView())
346
+
347
+ # اتصال البوت
348
+ if not guild.voice_client:
349
+ vc = await broadcast_vc.connect()
350
+ await asyncio.sleep(2) # استقرار الاتصال
351
+ await play_current_reciter(vc)
352
+
353
+ human_members = sum(1 for m in broadcast_vc.members if not m.bot)
354
+ if human_members == 0:
355
+ vc.pause()
356
+ await log_event(guild, "⏸️ القناة فارغة، تم وضع البث في وضع الاستعداد (لتجنب ضغط الشبكة).")
357
+ else:
358
+ await log_event(guild, "▶️ تم البدء بالبث لوجود مستمعين في القناة.")
359
+
360
+ except Exception as e:
361
+ print(f"⚠️ تحذير أثناء إنشاء القنوات (قد يكون بسبب ضغط الطلبات): {e}")
362
 
363
  @bot.event
364
  async def on_voice_state_update(member, before, after):
 
368
  bot_vc = guild.voice_client
369
  if not bot_vc: return
370
 
371
+ try:
372
+ # حالة دخول عضو
373
+ if after.channel and after.channel.id == bot_vc.channel.id:
374
+ if bot_vc.is_paused():
375
+ bot_vc.resume()
376
+ await log_event(guild, f"▶️ استئناف البث التلقائي لدخول مستمع.")
377
+
378
+ embed = discord.Embed(
379
+ title="أهلاً بك في مجلس الذكر 🕊️",
380
+ description=random.choice(REMINDERS),
381
+ color=discord.Color.gold()
382
+ )
383
+ embed.set_footer(text=f"القارئ الحالي: {RECITERS[current_reciter_index]['name']}")
384
+ try:
385
+ await after.channel.send(content=f"مرحباً بك {member.mention}", embed=embed, delete_after=60)
386
+ except: pass
387
+
388
+ # حالة خروج عضو
389
+ elif before.channel and before.channel.id == bot_vc.channel.id:
390
+ human_members = sum(1 for m in before.channel.members if not m.bot)
391
+ if human_members == 0 and bot_vc.is_playing():
392
+ bot_vc.pause()
393
+ except: pass # منع أي خطأ مفاجئ من إيقاف البوت
394
 
395
+ # =======================================================
396
+ # مهمة الفحص الهادئ (Heartbeat)
397
+ # =======================================================
398
+ @tasks.loop(minutes=5)
399
+ async def heartbeat_task():
400
+ """هذه الدالة تفحص البوت كل 5 دقائق لضمان عدم فصله من ديسكورد"""
401
+ try:
402
+ guild = bot.get_guild(TARGET_GUILD_ID)
403
+ if guild and guild.voice_client:
404
+ human_members = sum(1 for m in guild.voice_client.channel.members if not m.bot)
405
+ # إذا كان هناك ناس والصوت متوقف بالخطأ، نقوم بتشغيله
406
+ if human_members > 0 and not guild.voice_client.is_playing() and not guild.voice_client.is_paused():
407
+ await play_current_reciter(guild.voice_client)
408
+ await log_event(guild, "🔄 تم إعادة إنعاش البث التلقائي بواسطة نظام الحماية (Heartbeat).")
409
+ except: pass
410
 
411
  # =======================================================
412
  # أوامر السلاش (محصورة في غرفة التحكم)
 
416
  @is_owner_and_in_control()
417
  async def play_radio(interaction: discord.Interaction):
418
  vc = interaction.guild.voice_client
419
+ try:
420
+ if not vc: return await interaction.response.send_message("❌ البوت غير متصل.", ephemeral=True)
421
+ if vc.is_paused(): vc.resume()
422
+ else: await play_current_reciter(vc)
423
+ await interaction.response.send_message(f"▶️ تم التشغيل: **{RECITERS[current_reciter_index]['name']}**", ephemeral=True)
424
+ await log_event(interaction.guild, f"▶️ تم التشغيل عبر أمر /play_radio")
425
+ except: pass
426
 
427
  @bot.tree.command(name="stop_radio", description="⏹️ إيقاف البث مؤقتاً")
428
  @is_owner_and_in_control()
429
  async def stop_radio(interaction: discord.Interaction):
430
  vc = interaction.guild.voice_client
431
+ try:
432
+ if vc and vc.is_playing():
433
+ vc.pause()
434
+ await interaction.response.send_message("⏹️ تم الإيقاف.", ephemeral=True)
435
+ await log_event(interaction.guild, f"⏹️ تم الإيقاف عبر أمر /stop_radio")
436
+ else:
437
+ await interaction.response.send_message("⚠️ متوقف بالفعل.", ephemeral=True)
438
+ except: pass
439
 
440
  @bot.tree.command(name="volume", description="🔊 التحكم بدرجة الصوت (من 1 إلى 100)")
441
  @app_commands.describe(level="درجة الصوت من 1 إلى 100")
442
  @is_owner_and_in_control()
443
  async def set_volume(interaction: discord.Interaction, level: int):
444
  global current_volume
445
+ try:
446
+ if level < 1 or level > 100:
447
+ return await interaction.response.send_message("❌ يرجى إدخال رقم بين 1 و 100.", ephemeral=True)
 
 
 
448
 
449
+ current_volume = level / 100.0
450
+ vc = interaction.guild.voice_client
451
+ if vc and vc.source: vc.source.volume = current_volume
452
+
453
+ await interaction.response.send_message(f"🔊 تم تغيير درجة الصوت إلى: **{level}%**", ephemeral=True)
454
+ await log_event(interaction.guild, f"🔊 تم تغيير درجة الصوت إلى {level}%")
455
+ except: pass
456
 
457
  # =======================================================
458
  # التشغيل والـ Keep Alive