Spaces:
No application file
No application file
| import discord | |
| from discord import app_commands | |
| from discord.ext import commands | |
| import datetime | |
| import random | |
| import string | |
| import io | |
| import asyncio | |
| import re | |
| # --- الإعدادات الثابتة --- | |
| OWNER_ID = 1429183440485486679 | |
| ERROR_LOG_CHANNEL_ID = 1488536752691085552 | |
| TICKETS_CHANNEL_ID = 1488536530019549344 | |
| LOG_CHANNEL_ID = 1488536921813680218 | |
| STAFF_ROLES_IDS = [ | |
| 1488187501142216826, 1488187641424773140, 1488187816201687181, | |
| 1488188612313874733, 1488537308700344490, 1488537566910218260 | |
| ] | |
| PING_ROLES = "<@&1488187816201687181> <@&1488188612313874733> <@&1488537308700344490> <@&1488537566910218260>" | |
| # --- نظام الحماية والقيود --- | |
| active_tickets = {} # user_id: thread_id | |
| cooldowns = {} # user_id: datetime | |
| class MyBot(commands.Bot): | |
| def __init__(self): | |
| intents = discord.Intents.default() | |
| intents.message_content = True | |
| intents.members = True | |
| super().__init__(command_prefix="!", intents=intents) | |
| async def setup_hook(self): | |
| self.add_view(TicketPanelView()) | |
| self.add_view(TicketControls()) | |
| await self.tree.sync() | |
| async def on_error(self, event, *args, **kwargs): | |
| channel = self.get_channel(ERROR_LOG_CHANNEL_ID) | |
| if channel: | |
| await channel.send(f"⚠️ **Crash Detected / خطأ في النظام**\n`{event}`\n<@{OWNER_ID}>") | |
| bot = MyBot() | |
| # --- أدوات مساعدة --- | |
| def is_staff(interaction: discord.Interaction): | |
| return any(role.id in STAFF_ROLES_IDS for role in interaction.user.roles) or interaction.user.id == OWNER_ID | |
| # --- نظام واجهة التذاكر --- | |
| class TicketPanelView(discord.ui.View): | |
| def __init__(self): | |
| super().__init__(timeout=None) | |
| async def select_callback(self, interaction: discord.Interaction, select: discord.ui.Select): | |
| user_id = interaction.user.id | |
| # تحقق من التذكرة المفتوحة | |
| if user_id in active_tickets: | |
| return await interaction.response.send_message("❌ You already have an open ticket! / لديك تذكرة مفتوحة بالفعل!", ephemeral=True) | |
| # تحقق من الـ Cooldown | |
| if user_id in cooldowns: | |
| diff = (datetime.datetime.now() - cooldowns[user_id]).total_seconds() | |
| if diff < 60: | |
| return await interaction.response.send_message(f"⏳ Wait {int(60-diff)}s / انتظر {int(60-diff)} ثانية", ephemeral=True) | |
| await interaction.response.defer(ephemeral=True) | |
| # إنشاء Thread | |
| channel = bot.get_channel(TICKETS_CHANNEL_ID) | |
| ticket_id = ''.join(random.choices(string.ascii_lowercase + string.digits, k=4)) | |
| thread_name = f"ticket-{random.randint(1000,9999)}-{ticket_id}" | |
| thread = await channel.create_thread( | |
| name=thread_name, | |
| type=discord.ChannelType.private_thread, | |
| invitable=False | |
| ) | |
| active_tickets[user_id] = thread.id | |
| await thread.add_user(interaction.user) | |
| await thread.edit(slowmode_delay=10) | |
| # رسالة الترحيب | |
| embed = discord.Embed( | |
| title="Ticket Created / تم إنشاء التذكرة", | |
| description=f"Welcome {interaction.user.mention}\n{PING_ROLES}\nYour ticket has been created, please wait...\n\nمرحباً بك، تم إنشاء تذكرتك، يرجى الانتظار...", | |
| color=discord.Color.blue() | |
| ) | |
| await thread.send(embed=embed, view=TicketControls()) | |
| # إرسال DM | |
| try: | |
| await interaction.user.send(f"✅ Your ticket has been created: {thread.jump_url}\nتم إنشاء تذكرتك بنجاح.") | |
| except: pass | |
| await interaction.followup.send(f"✅ Ticket Created: {thread.mention}", ephemeral=True) | |
| class TicketControls(discord.ui.View): | |
| def __init__(self): | |
| super().__init__(timeout=None) | |
| async def claim(self, interaction: discord.Interaction, button: discord.ui.Button): | |
| if not is_staff(interaction): | |
| return await interaction.response.send_message("❌ Staff only / للموظفين فقط", ephemeral=True) | |
| button.disabled = True | |
| await interaction.response.edit_message(view=self) | |
| await interaction.followup.send(f"📌 Ticket claimed by / تم استلام التذكرة من قبل: {interaction.user.mention}") | |
| # إرسال DM للمستخدم | |
| thread_owner_id = next((k for k, v in active_tickets.items() if v == interaction.channel_id), None) | |
| if thread_owner_id: | |
| user = await bot.fetch_user(thread_owner_id) | |
| try: await user.send(f"👋 Your ticket has been claimed by **{interaction.user.name}**\nتم استلام تذكرتك بواسطة الموظف.") | |
| except: pass | |
| async def close(self, interaction: discord.Interaction, button: discord.ui.Button): | |
| if not is_staff(interaction): | |
| return await interaction.response.send_message("❌ Staff only / للموظفين فقط", ephemeral=True) | |
| await interaction.response.send_modal(CloseModal()) | |
| class CloseModal(discord.ui.Modal, title="Close Ticket / إغلاق التذكرة"): | |
| reason = discord.ui.TextInput(label="Reason / السبب", placeholder="Enter closing reason...", min_length=5, required=True) | |
| async def on_submit(self, interaction: discord.Interaction): | |
| thread = interaction.channel | |
| messages = [msg async for msg in thread.history(limit=None, oldest_first=True)] | |
| # إنشاء ملف سجل | |
| transcript = "" | |
| for m in messages: | |
| transcript += f"[{m.created_at.strftime('%Y-%m-%d %H:%M')}] {m.author}: {m.content}\n" | |
| file = discord.File(io.BytesIO(transcript.encode()), filename=f"{thread.name}.txt") | |
| # إرسال اللوق | |
| log_channel = bot.get_channel(LOG_CHANNEL_ID) | |
| user_id = next((k for k, v in active_tickets.items() if v == thread.id), "Unknown") | |
| embed = discord.Embed(title="Ticket Closed / إغلاق تذكرة", color=discord.Color.red()) | |
| embed.add_field(name="User / المستخدم", value=f"<@{user_id}>") | |
| embed.add_field(name="Staff / الموظف", value=interaction.user.mention) | |
| embed.add_field(name="Reason / السبب", value=self.reason.value) | |
| embed.add_field(name="Open Time / وقت الفتح", value=thread.created_at.strftime('%Y-%m-%d %H:%M')) | |
| await log_channel.send(embed=embed, file=file) | |
| # تنظيف البيانات | |
| if user_id != "Unknown": | |
| active_tickets.pop(int(user_id), None) | |
| cooldowns[int(user_id)] = datetime.datetime.now() | |
| await interaction.response.send_message("Closing... / جارِ الإغلاق...") | |
| await thread.delete() | |
| # --- الأوامر --- | |
| async def setup_ticket(interaction: discord.Interaction): | |
| if interaction.user.id != OWNER_ID: | |
| return await interaction.response.send_message("❌ Access Denied", ephemeral=True) | |
| embed = discord.Embed( | |
| title="Support System / نظام الدعم الفني", | |
| description=( | |
| "**Rules / القوانين:**\n" | |
| "❌ No spamming tickets / يمنع فتح تذاكر عشوائية\n" | |
| "✅ Use for complaints or support / استخدم التذاكر للشكاوى والدعم فقط\n\n" | |
| "**Options / الخيارات:**\n" | |
| "• Staff Complaint / شكوى إداري\n" | |
| "• User Complaint / شكوى لاعب\n" | |
| "• Bug Report / بلاغ ثغرة\n" | |
| "• Technical Support / دعم فني" | |
| ), | |
| color=discord.Color.green() | |
| ) | |
| await interaction.response.send_message("Panel Sent!", ephemeral=True) | |
| await interaction.channel.send(embed=embed, view=TicketPanelView()) | |
| async def help_cmd(interaction: discord.Interaction): | |
| await interaction.response.send_message(f"To open a ticket, go to <#{TICKETS_CHANNEL_ID}>\nلفتح تذكرة، توجه إلى القناة المخصصة.", ephemeral=True) | |
| # --- نظام الحماية (Anti-Spam & Content Filter) --- | |
| async def on_message(message): | |
| if message.author.bot: return | |
| if not isinstance(message.channel, discord.Thread): return | |
| if message.channel.parent_id != TICKETS_CHANNEL_ID: return | |
| # منع الروابط، الملفات، والبصمات الصوتية | |
| if re.search(r'http[s]?://', message.content) or message.attachments or message.flags.voice: | |
| # السماح فقط بالصور والفيديو | |
| allowed = True | |
| if message.attachments: | |
| for att in message.attachments: | |
| if not att.content_type or (not att.content_type.startswith('image/') and not att.content_type.startswith('video/')): | |
| allowed = False | |
| if not allowed or re.search(r'http[s]?://', message.content) or message.flags.voice: | |
| await message.delete() | |
| return await message.channel.send(f"{message.author.mention} ❌ Only text, images, and videos are allowed.\nمسموح فقط بالنصوص، الصور، والفيديو.", delete_after=5) | |
| # منع السبام (أحرف عشوائية طويلة) | |
| if len(message.content) > 50 and len(set(message.content)) < 10: | |
| await message.delete() | |
| return await message.channel.send("❌ Random characters/spam detected.", delete_after=5) | |
| import os | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| bot.run(os.getenv('TOKEN')) | |