File size: 10,730 Bytes
ad835d2
 
c1cc14b
 
ad835d2
 
c1cc14b
 
ad835d2
 
c1cc14b
ad835d2
c1cc14b
 
 
 
 
 
ad835d2
c1cc14b
ad835d2
c1cc14b
 
 
ad835d2
c1cc14b
 
 
 
 
 
ad835d2
c1cc14b
 
 
 
ad835d2
c1cc14b
 
 
 
ad835d2
c1cc14b
ad835d2
c1cc14b
 
 
ad835d2
c1cc14b
ad835d2
 
 
 
c1cc14b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ad835d2
c1cc14b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ad835d2
c1cc14b
ad835d2
 
 
c1cc14b
 
 
 
 
 
 
 
 
ad835d2
c1cc14b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ad835d2
 
c1cc14b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ad835d2
c1cc14b
 
 
 
 
 
 
 
ad835d2
c1cc14b
ad835d2
c1cc14b
 
ad835d2
c1cc14b
 
 
ad835d2
c1cc14b
ad835d2
c1cc14b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ad835d2
c1cc14b
ad835d2
c1cc14b
 
 
 
ad835d2
c1cc14b
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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)

    @discord.ui.select(
        custom_id="ticket_select",
        placeholder="Choose ticket type / اختر نوع التذكرة",
        options=[
            discord.SelectOption(label="Complaint against staff / شكوى ضد إداري", value="staff_complaint", emoji="⚖️"),
            discord.SelectOption(label="Complaint against user / شكوى ضد لاعب", value="user_complaint", emoji="👥"),
            discord.SelectOption(label="Bug report / بلاغ عن ثغرة", value="bug_report", emoji="🐛"),
            discord.SelectOption(label="Technical support / دعم فني", value="tech_support", emoji="🛠️"),
        ]
    )
    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)

    @discord.ui.button(label="Claim / استلام", style=discord.ButtonStyle.green, custom_id="claim_btn")
    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

    @discord.ui.button(label="Close / إغلاق", style=discord.ButtonStyle.red, custom_id="close_btn")
    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()

# --- الأوامر ---
@bot.tree.command(name="setup-ticket-panel", description="Setup the ticket panel (Owner Only)")
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())

@bot.tree.command(name="help", description="Show help information")
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) ---
@bot.event
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'))