Skydata001 commited on
Commit
c1cc14b
ยท
verified ยท
1 Parent(s): 8876987

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +189 -988
main.py CHANGED
@@ -1,1024 +1,225 @@
1
- """
2
- โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
3
- โ•‘ Discord Ticket Bot - Production Ready โ•‘
4
- โ•‘ Built with discord.py for Hugging Face โ•‘
5
- โ•‘ Author: Professional Discord Bot Developer โ•‘
6
- โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
7
-
8
- ุงู„ูˆุตู: ู†ุธุงู… ุชุฐุงูƒุฑ ู…ุชูƒุงู…ู„ ุซู†ุงุฆูŠ ุงู„ู„ุบุฉ (ุนุฑุจูŠ/ุฅู†ุฌู„ูŠุฒูŠ)
9
- Description: Full bilingual ticket system (Arabic/English)
10
- """
11
-
12
  import discord
13
- from discord.ext import commands
14
  from discord import app_commands
15
- import asyncio
16
- import os
17
- import sys
18
- import traceback
19
- import logging
20
- import json
21
  import random
22
  import string
23
- import datetime
 
24
  import re
25
- from collections import defaultdict
26
-
27
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
28
- # LOGGING SETUP | ุฅุนุฏุงุฏ ุงู„ุณุฌู„
29
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
30
- logging.basicConfig(
31
- level=logging.INFO,
32
- format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
33
- handlers=[
34
- logging.StreamHandler(sys.stdout),
35
- logging.FileHandler("bot.log", encoding="utf-8"),
36
- ],
37
- )
38
- log = logging.getLogger("TicketBot")
39
-
40
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
41
- # CONFIGURATION | ุงู„ุฅุนุฏุงุฏุงุช
42
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
43
- TOKEN = os.getenv("TOKEN") # โ† ูŠููˆุถุน ููŠ Environment Variables ุนู„ู‰ Hugging Face
44
 
 
45
  OWNER_ID = 1429183440485486679
46
- ERROR_CHANNEL_ID = 1488536752691085552 # ู‚ู†ุงุฉ ุฅุฑุณุงู„ ุงู„ุฃุฎุทุงุก
47
- TICKET_CHANNEL_ID = 1488536530019549344 # ุงู„ู‚ู†ุงุฉ ุงู„ุชูŠ ุชูู†ุดุฃ ููŠู‡ุง ุงู„ู€ Threads
48
- LOG_CHANNEL_ID = 1488536921813680218 # ู‚ู†ุงุฉ ุงู„ู„ูˆู‚
49
-
50
- # ุฑุชุจ ุงู„ู…ูˆุธููŠู† ุงู„ุชูŠ ุชูู†ุจู‘ู‡ ุฏุงุฎู„ ุงู„ุชุฐูƒุฑุฉ
51
- STAFF_ROLE_PING_IDS = [
52
- 1488187816201687181,
53
- 1488188612313874733,
54
- 1488537308700344490,
55
- 1488537566910218260,
56
  ]
 
57
 
58
- # ุฑุชุจ ู„ุฏูŠู‡ุง ุตู„ุงุญูŠุฉ Claim ูˆ Close
59
- ALLOWED_ROLE_IDS = [
60
- 1488187501142216826,
61
- 1488187641424773140,
62
- 1488187816201687181,
63
- 1488188612313874733,
64
- 1488537308700344490,
65
- 1488537566910218260,
66
- ]
67
-
68
- TICKET_SLOWMODE = 10 # ุซูˆุงู†ูŠ
69
- COOLDOWN_SECONDS = 60 # ุซูˆุงู†ูŠ ุจุนุฏ ุฅุบู„ุงู‚ ุงู„ุชุฐูƒุฑุฉ
70
-
71
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
72
- # IN-MEMORY STORAGE | ุงู„ุชุฎุฒูŠู† ููŠ ุงู„ุฐุงูƒุฑุฉ
73
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
74
- # { user_id: thread_id }
75
- active_tickets: dict[int, int] = {}
76
-
77
- # { thread_id: {"user_id", "category", "opened_at", "claimed_by", "ticket_name"} }
78
- ticket_data: dict[int, dict] = {}
79
-
80
- # { user_id: datetime } - ูˆู‚ุช ุงู†ุชู‡ุงุก ุงู„ู€ Cooldown
81
- cooldowns: dict[int, datetime.datetime] = {}
82
-
83
- # { user_id: [timestamps] } - ู„ูƒุดู ุงู„ุณุจุงู…
84
- spam_tracker: dict[int, list] = defaultdict(list)
85
-
86
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
87
- # HELPER FUNCTIONS | ุฏูˆุงู„ ู…ุณุงุนุฏุฉ
88
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
89
-
90
- def generate_ticket_name() -> str:
91
- """ุชูˆู„ูŠุฏ ุงุณู… ุนุดูˆุงุฆูŠ ู„ู„ุชุฐูƒุฑุฉ ู…ุซู„ ticket-4932-xa"""
92
- number = random.randint(1000, 9999)
93
- suffix = "".join(random.choices(string.ascii_lowercase, k=2))
94
- return f"ticket-{number}-{suffix}"
95
-
96
-
97
- def has_staff_role(member: discord.Member) -> bool:
98
- """ุงู„ุชุญู‚ู‚ ุฅุฐุง ูƒุงู† ุงู„ุนุถูˆ ู„ุฏูŠู‡ ุฑุชุจุฉ ู…ูˆุธู"""
99
- role_ids = {r.id for r in member.roles}
100
- return bool(role_ids.intersection(ALLOWED_ROLE_IDS))
101
-
102
-
103
- def is_owner(user: discord.User | discord.Member) -> bool:
104
- """ุงู„ุชุญู‚ู‚ ุฅุฐุง ูƒุงู† ุงู„ู…ุณุชุฎุฏู… ู‡ูˆ ุงู„ู…ุงู„ูƒ"""
105
- return user.id == OWNER_ID
106
-
107
 
108
- def now_utc() -> datetime.datetime:
109
- return datetime.datetime.now(datetime.timezone.utc)
110
-
111
-
112
- def format_duration(delta: datetime.timedelta) -> str:
113
- """ุชู†ุณูŠู‚ ุงู„ู…ุฏุฉ ุงู„ุฒู…ู†ูŠุฉ | Format duration"""
114
- total = int(delta.total_seconds())
115
- hours, remainder = divmod(total, 3600)
116
- minutes, seconds = divmod(remainder, 60)
117
- if hours:
118
- return f"{hours}h {minutes}m {seconds}s"
119
- elif minutes:
120
- return f"{minutes}m {seconds}s"
121
- else:
122
- return f"{seconds}s"
123
-
124
-
125
- def is_spam(user_id: int) -> bool:
126
- """
127
- ูƒุดู ุงู„ุณุจุงู…: ุฃูƒุซุฑ ู…ู† 5 ุฑุณุงุฆู„ ููŠ 5 ุซูˆุงู†ูŠ
128
- Spam detection: more than 5 messages in 5 seconds
129
- """
130
- now = now_utc()
131
- tracker = spam_tracker[user_id]
132
- # ุฅุฒุงู„ุฉ ุงู„ุฑุณุงุฆู„ ุงู„ู‚ุฏูŠู…ุฉ ุฃูƒุซุฑ ู…ู† 5 ุซูˆุงู†ูŠ
133
- spam_tracker[user_id] = [t for t in tracker if (now - t).total_seconds() < 5]
134
- spam_tracker[user_id].append(now)
135
- return len(spam_tracker[user_id]) > 5
136
-
137
-
138
- def contains_link(content: str) -> bool:
139
- """ูƒุดู ุงู„ุฑูˆุงุจุท ููŠ ุงู„ุฑุณุงู„ุฉ"""
140
- url_pattern = re.compile(
141
- r"(https?://|www\.|discord\.gg/|\.com|\.net|\.org|\.io)", re.IGNORECASE
142
- )
143
- return bool(url_pattern.search(content))
144
-
145
-
146
- def is_random_chars(content: str) -> bool:
147
- """
148
- ูƒุดู ุงู„ุญุฑูˆู ุงู„ุนุดูˆุงุฆูŠุฉ: ู†ุณุจุฉ ุงู„ุญุฑูˆู ุบูŠุฑ ุงู„ู…ุนุฑูˆูุฉ ุนุงู„ูŠุฉ
149
- Detect random character spam
150
- """
151
- if len(content) < 10:
152
- return False
153
- # ุฅุฐุง ูƒุงู†ุช ู†ุณุจุฉ ุงู„ุฃุญุฑู ุบูŠุฑ ุงู„ุฃุจุฌุฏูŠุฉ ุฃูˆ ุบูŠุฑ ุงู„ุนุฑุจูŠุฉ ุนุงู„ูŠุฉ ุฌุฏุงู‹
154
- alnum_count = sum(1 for c in content if c.isalnum() or "\u0600" <= c <= "\u06ff")
155
- return alnum_count / len(content) < 0.3
156
-
157
-
158
- def count_emojis(content: str) -> int:
159
- """ุนุฏู‘ ุงู„ุฅูŠู…ูˆุฌูŠ ููŠ ุงู„ุฑุณุงู„ุฉ"""
160
- emoji_pattern = re.compile(
161
- "[\U00010000-\U0010ffff]|[\u2600-\u26FF]|[\u2700-\u27BF]",
162
- flags=re.UNICODE,
163
- )
164
- return len(emoji_pattern.findall(content))
165
-
166
-
167
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
168
- # BOT SETUP | ุฅุนุฏุงุฏ ุงู„ุจูˆุช
169
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
170
- intents = discord.Intents.default()
171
- intents.message_content = True
172
- intents.members = True
173
- intents.guilds = True
174
-
175
- bot = commands.Bot(command_prefix="!", intents=intents, help_command=None)
176
 
 
 
 
 
177
 
178
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
179
- # CATEGORY SELECT MENU | ู‚ุงุฆู…ุฉ ุงุฎุชูŠุงุฑ ุงู„ูุฆุฉ
180
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
181
- class TicketCategorySelect(discord.ui.Select):
182
- def __init__(self):
183
- options = [
184
- discord.SelectOption(
185
- label="Complaint against staff | ุดูƒูˆู‰ ุถุฏ ู…ูˆุธู",
186
- description="Report a staff member / ุงู„ุฅุจู„ุงุบ ุนู† ู…ูˆุธู",
187
- value="complaint_staff",
188
- emoji="๐Ÿ‘ฎ",
189
- ),
190
- discord.SelectOption(
191
- label="Complaint against user | ุดูƒูˆู‰ ุถุฏ ู…ุณุชุฎุฏู…",
192
- description="Report another user / ุงู„ุฅุจู„ุงุบ ุนู† ู…ุณุชุฎุฏู…",
193
- value="complaint_user",
194
- emoji="๐Ÿšจ",
195
- ),
196
- discord.SelectOption(
197
- label="Bug report | ุงู„ุฅุจู„ุงุบ ุนู† ุฎุทุฃ",
198
- description="Report a bug / ุงู„ุฅุจู„ุงุบ ุนู† ู…ุดูƒู„ุฉ ุชู‚ู†ูŠุฉ",
199
- value="bug_report",
200
- emoji="๐Ÿ›",
201
- ),
202
- discord.SelectOption(
203
- label="Technical support | ุงู„ุฏุนู… ุงู„ุชู‚ู†ูŠ",
204
- description="Get technical help / ุทู„ุจ ู…ุณุงุนุฏุฉ ุชู‚ู†ูŠุฉ",
205
- value="technical_support",
206
- emoji="๐Ÿ”ง",
207
- ),
208
- ]
209
- super().__init__(
210
- placeholder="๐Ÿ“‹ ุงุฎุชุฑ ู†ูˆุน ุงู„ุชุฐูƒุฑุฉ | Choose ticket type",
211
- min_values=1,
212
- max_values=1,
213
- options=options,
214
- custom_id="ticket_category_select",
215
- )
216
 
217
- async def callback(self, interaction: discord.Interaction):
218
- await handle_ticket_creation(interaction, self.values[0])
219
 
 
 
 
220
 
 
221
  class TicketPanelView(discord.ui.View):
222
- """View ุงู„ุซุงุจุช ู„ู€ Panel ุงู„ุชุฐุงูƒุฑ"""
223
-
224
  def __init__(self):
225
  super().__init__(timeout=None)
226
- self.add_item(TicketCategorySelect())
227
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
230
- # TICKET CONTROL BUTTONS | ุฃุฒุฑุงุฑ ุงู„ุชุญูƒู… ููŠ ุงู„ุชุฐูƒุฑุฉ
231
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
232
- class TicketControlView(discord.ui.View):
233
- """ุฃุฒุฑุงุฑ Claim ูˆ Close ุฏุงุฎู„ ุงู„ุชุฐูƒุฑุฉ"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
 
 
235
  def __init__(self):
236
  super().__init__(timeout=None)
237
 
238
- @discord.ui.button(
239
- label="โœ… Claim Ticket | ุงุณุชู„ุงู… ุงู„ุชุฐูƒุฑุฉ",
240
- style=discord.ButtonStyle.success,
241
- custom_id="ticket_claim",
242
- )
243
- async def claim_button(self, interaction: discord.Interaction, button: discord.ui.Button):
244
- if not has_staff_role(interaction.user):
245
- await interaction.response.send_message(
246
- "โŒ **ู„ูŠุณ ู„ุฏูŠูƒ ุตู„ุงุญูŠุฉ ู„ุงุณุชู„ุงู… ุงู„ุชุฐุงูƒุฑ.**\n"
247
- "โŒ **You don't have permission to claim tickets.**",
248
- ephemeral=True,
249
- )
250
- return
251
-
252
- thread_id = interaction.channel.id
253
- data = ticket_data.get(thread_id)
254
- if not data:
255
- await interaction.response.send_message(
256
- "โŒ ุจูŠุงู†ุงุช ุงู„ุชุฐูƒุฑุฉ ุบูŠุฑ ู…ูˆุฌูˆุฏุฉ. | Ticket data not found.", ephemeral=True
257
- )
258
- return
259
-
260
- if data.get("claimed_by"):
261
- await interaction.response.send_message(
262
- f"โš ๏ธ **ู‡ุฐู‡ ุงู„ุชุฐูƒุฑุฉ ู…ุณุชู„ู…ุฉ ุจุงู„ูุนู„ ู…ู† ู‚ูุจู„:** <@{data['claimed_by']}>\n"
263
- f"โš ๏ธ **This ticket is already claimed by:** <@{data['claimed_by']}>",
264
- ephemeral=True,
265
- )
266
- return
267
-
268
- # ุชุณุฌูŠู„ ุงู„ู€ Claim
269
- ticket_data[thread_id]["claimed_by"] = interaction.user.id
270
-
271
  # ุฅุฑุณุงู„ DM ู„ู„ู…ุณุชุฎุฏู…
272
- user_id = data["user_id"]
273
- guild = interaction.guild
274
- member = guild.get_member(user_id)
275
- if member:
276
- try:
277
- dm_embed = discord.Embed(
278
- title="โœ… ุชู… ุงุณุชู„ุงู… ุชุฐูƒุฑุชูƒ | Your Ticket Has Been Claimed",
279
- description=(
280
- f"๐Ÿ‡ธ๐Ÿ‡ฆ **ุชู… ุงุณุชู„ุงู… ุชุฐูƒุฑุชูƒ ู…ู† ู‚ูุจู„:** {interaction.user.display_name}\n\n"
281
- f"๐Ÿ‡ฌ๐Ÿ‡ง **Your ticket has been claimed by:** {interaction.user.display_name}\n\n"
282
- f"๐Ÿ“‹ **ุงู„ุชุฐูƒุฑุฉ:** `{data['ticket_name']}`\n"
283
- f"โฐ **ุงู„ูˆู‚ุช:** <t:{int(now_utc().timestamp())}:F>"
284
- ),
285
- color=discord.Color.green(),
286
- timestamp=now_utc(),
287
- )
288
- dm_embed.set_footer(text="Ticket System | ู†ุธุงู… ุงู„ุชุฐุงูƒุฑ")
289
- await member.send(embed=dm_embed)
290
- except discord.Forbidden:
291
- pass # DM ู…ุบู„ู‚
292
-
293
- # ุชุญุฏูŠุซ ุฑุณุงู„ุฉ ุงู„ุชุญูƒู…
294
- claim_embed = discord.Embed(
295
- title="โœ… ุชู… ุงุณุชู„ุงู… ุงู„ุชุฐูƒุฑุฉ | Ticket Claimed",
296
- description=(
297
- f"๐Ÿ‡ธ๐Ÿ‡ฆ **ุชู… ุงุณุชู„ุงู… ู‡ุฐู‡ ุงู„ุชุฐูƒุฑุฉ ู…ู† ู‚ูุจู„:** {interaction.user.mention}\n"
298
- f"๐Ÿ‡ฌ๐Ÿ‡ง **This ticket has been claimed by:** {interaction.user.mention}"
299
- ),
300
- color=discord.Color.green(),
301
- timestamp=now_utc(),
302
- )
303
- await interaction.response.send_message(embed=claim_embed)
304
-
305
- log.info(f"Ticket {data['ticket_name']} claimed by {interaction.user} ({interaction.user.id})")
306
-
307
- @discord.ui.button(
308
- label="๐Ÿ”’ Close Ticket | ุฅุบู„ุงู‚ ุงู„ุชุฐูƒุฑุฉ",
309
- style=discord.ButtonStyle.danger,
310
- custom_id="ticket_close",
311
- )
312
- async def close_button(self, interaction: discord.Interaction, button: discord.ui.Button):
313
- if not has_staff_role(interaction.user):
314
- await interaction.response.send_message(
315
- "โŒ **ู„ูŠุณ ู„ุฏูŠูƒ ุตู„ุงุญูŠุฉ ู„ุฅุบู„ุงู‚ ุงู„ุชุฐุงูƒุฑ.**\n"
316
- "โŒ **You don't have permission to close tickets.**",
317
- ephemeral=True,
318
- )
319
- return
320
-
321
- # ุฅุฑุณุงู„ Modal ู„ุณุจุจ ุงู„ุฅุบู„ุงู‚
322
- modal = CloseReasonModal(interaction.channel)
323
- await interaction.response.send_modal(modal)
324
-
325
-
326
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
327
- # CLOSE REASON MODAL | ู†ุงูุฐุฉ ุณุจุจ ุงู„ุฅุบู„ุงู‚
328
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
329
- class CloseReasonModal(discord.ui.Modal, title="๐Ÿ”’ Close Ticket | ุฅุบู„ุงู‚ ุงู„ุชุฐูƒุฑุฉ"):
330
- reason = discord.ui.TextInput(
331
- label="ุณุจุจ ุงู„ุฅุบู„ุงู‚ | Close Reason",
332
- placeholder="ุงูƒุชุจ ุณุจุจ ุฅุบู„ุงู‚ ุงู„ุชุฐูƒุฑุฉ... | Enter the reason for closing...",
333
- style=discord.TextStyle.paragraph,
334
- min_length=5,
335
- max_length=500,
336
- required=True,
337
- )
338
-
339
- def __init__(self, thread: discord.Thread):
340
- super().__init__()
341
- self.thread = thread
342
 
343
  async def on_submit(self, interaction: discord.Interaction):
344
- await interaction.response.defer(ephemeral=True)
345
- await close_ticket(interaction, self.thread, self.reason.value)
346
-
347
-
348
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
349
- # CORE: CREATE TICKET | ุฅู†ุดุงุก ุงู„ุชุฐูƒุฑุฉ
350
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
351
- async def handle_ticket_creation(interaction: discord.Interaction, category: str):
352
- """ุงู„ู…ู†ุทู‚ ุงู„ุฑุฆูŠุณูŠ ู„ุฅู†ุดุงุก ุชุฐูƒุฑุฉ ุฌุฏูŠุฏุฉ"""
353
- user = interaction.user
354
- guild = interaction.guild
355
-
356
- # โ”€โ”€ 1. ุงู„ุชุญู‚ู‚ ู…ู† Cooldown โ”€โ”€
357
- if user.id in cooldowns:
358
- remaining = (cooldowns[user.id] - now_utc()).total_seconds()
359
- if remaining > 0:
360
- await interaction.response.send_message(
361
- f"โณ **ูŠุฌุจ ุงู„ุงู†ุชุธุงุฑ {int(remaining)} ุซุงู†ูŠุฉ ู‚ุจู„ ูุชุญ ุชุฐูƒุฑุฉ ุฌุฏูŠุฏุฉ.**\n"
362
- f"โณ **Please wait {int(remaining)} seconds before opening a new ticket.**",
363
- ephemeral=True,
364
- )
365
- return
366
-
367
- # โ”€โ”€ 2. ุงู„ุชุญู‚ู‚ ู…ู† ูˆุฌูˆุฏ ุชุฐูƒุฑุฉ ู…ูุชูˆุญุฉ โ”€โ”€
368
- if user.id in active_tickets:
369
- thread_id = active_tickets[user.id]
370
- await interaction.response.send_message(
371
- f"โš ๏ธ **ู„ุฏูŠูƒ ุชุฐูƒุฑุฉ ู…ูุชูˆุญุฉ ุจุงู„ูุนู„:** <#{thread_id}>\n"
372
- f"โš ๏ธ **You already have an open ticket:** <#{thread_id}>",
373
- ephemeral=True,
374
- )
375
- return
376
-
377
- # โ”€โ”€ 3. ุฑุณุงู„ุฉ ุชุฃูƒูŠุฏ ู„ู„ู…ุณุชุฎุฏู… โ”€โ”€
378
- category_labels = {
379
- "complaint_staff": "๐Ÿ‘ฎ ุดูƒูˆู‰ ุถุฏ ู…ูˆุธู | Complaint against staff",
380
- "complaint_user": "๐Ÿšจ ุดูƒูˆู‰ ุถุฏ ู…ุณุชุฎุฏู… | Complaint against user",
381
- "bug_report": "๐Ÿ› ุงู„ุฅุจู„ุงุบ ุนู† ุฎุทุฃ | Bug report",
382
- "technical_support": "๐Ÿ”ง ุงู„ุฏุนู… ุงู„ุชู‚ู†ูŠ | Technical support",
383
- }
384
-
385
- await interaction.response.send_message(
386
- embed=discord.Embed(
387
- title="โณ ุฌุงุฑูŠ ุฅู†ุดุงุก ุชุฐูƒุฑุชูƒ... | Creating your ticket...",
388
- description=(
389
- f"๐Ÿ‡ธ๐Ÿ‡ฆ **ุงู„ูุฆุฉ:** {category_labels.get(category, category)}\n"
390
- f"๐Ÿ‡ฌ๐Ÿ‡ง **Category:** {category_labels.get(category, category)}\n\n"
391
- "ูŠูุฑุฌู‰ ุงู„ุงู†ุชุธุงุฑ... | Please wait..."
392
- ),
393
- color=discord.Color.yellow(),
394
- ),
395
- ephemeral=True,
396
- )
397
-
398
- # โ”€โ”€ 4. ุงู„ุญุตูˆู„ ุนู„ู‰ ู‚ู†ุงุฉ ุงู„ุชุฐุงูƒุฑ โ”€โ”€
399
- ticket_channel = guild.get_channel(TICKET_CHANNEL_ID)
400
- if not ticket_channel:
401
- try:
402
- ticket_channel = await guild.fetch_channel(TICKET_CHANNEL_ID)
403
- except Exception:
404
- await interaction.followup.send(
405
- "โŒ ู„ู… ูŠุชู… ุงู„ุนุซูˆุฑ ุนู„ู‰ ู‚ู†ุงุฉ ุงู„ุชุฐุงูƒุฑ. ุชูˆุงุตู„ ู…ุน ุงู„ุฅุฏุงุฑุฉ.\n"
406
- "โŒ Ticket channel not found. Contact an admin.",
407
- ephemeral=True,
408
- )
409
- return
410
-
411
- # โ”€โ”€ 5. ุฅู†ุดุงุก Thread ุฎุงุต โ”€โ”€
412
- ticket_name = generate_ticket_name()
413
- opened_at = now_utc()
414
-
415
- try:
416
- thread = await ticket_channel.create_thread(
417
- name=ticket_name,
418
- type=discord.ChannelType.private_thread,
419
- auto_archive_duration=10080, # 7 ุฃูŠุงู…
420
- invitable=False, # ู„ุง ูŠู…ูƒู† ู„ู„ุฃุนุถุงุก ุฏุนูˆุฉ ุขุฎุฑูŠู†
421
- reason=f"Ticket by {user} ({user.id}) - {category}",
422
- )
423
- except discord.Forbidden:
424
- await interaction.followup.send(
425
- "โŒ **ู„ุง ุฃู…ู„ูƒ ุตู„ุงุญูŠุฉ ุฅู†ุดุงุก Thread. ุชูˆุงุตู„ ู…ุน ุงู„ุฅุฏุงุฑุฉ.**\n"
426
- "โŒ **Missing permission to create threads. Contact an admin.**",
427
- ephemeral=True,
428
- )
429
- return
430
- except Exception as e:
431
- log.error(f"Error creating thread: {e}")
432
- await interaction.followup.send(
433
- "โŒ ุญุฏุซ ุฎุทุฃ ุนู†ุฏ ุฅู†ุดุงุก ุงู„ุชุฐูƒุฑุฉ. | An error occurred creating the ticket.",
434
- ephemeral=True,
435
- )
436
- return
437
-
438
- # โ”€โ”€ 6. ุฅุถุงูุฉ ุงู„ู…ุณุชุฎุฏู… ู„ู„ู€ Thread โ”€โ”€
439
- try:
440
- await thread.add_user(user)
441
- except Exception as e:
442
- log.warning(f"Could not add user to thread: {e}")
443
-
444
- # โ”€โ”€ 7. ุถุจุท Slow Mode โ”€โ”€
445
- try:
446
- await thread.edit(slowmode_delay=TICKET_SLOWMODE)
447
- except Exception:
448
- pass
449
-
450
- # โ”€โ”€ 8. ุชุณุฌูŠู„ ุจูŠุงู†ุงุช ุงู„ุชุฐูƒุฑุฉ โ”€โ”€
451
- ticket_data[thread.id] = {
452
- "user_id": user.id,
453
- "category": category,
454
- "opened_at": opened_at,
455
- "claimed_by": None,
456
- "ticket_name": ticket_name,
457
- "messages": [], # ู„ุชุชุจุน ุงู„ู…ุญุงุฏุซุฉ
458
- }
459
- active_tickets[user.id] = thread.id
460
-
461
- # โ”€โ”€ 9. ุจู†ุงุก ุฑุณุงู„ุฉ ุงู„ุชุฑุญูŠุจ ุฏุงุฎู„ ุงู„ุชุฐูƒุฑุฉ โ”€โ”€
462
- welcome_embed = discord.Embed(
463
- title=f"๐ŸŽซ {ticket_name}",
464
- description=(
465
- "โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n"
466
- f"๐Ÿ‡ธ๐Ÿ‡ฆ **ู…ุฑุญุจุงู‹ {user.mention}!**\n"
467
- f"ุชู… ุฅู†ุดุงุก ุชุฐูƒุฑุชูƒ ุจู†ุฌุงุญ. ูŠูุฑุฌู‰ ุดุฑุญ ู…ุดูƒู„ุชูƒ ูˆุณู†ู‚ูˆู… ุจู…ุณุงุนุฏุชูƒ ููŠ ุฃู‚ุฑุจ ูˆู‚ุช.\n\n"
468
- f"๐Ÿ‡ฌ๐Ÿ‡ง **Welcome {user.mention}!**\n"
469
- f"Your ticket has been created. Please describe your issue and we'll assist you shortly.\n"
470
- "โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n"
471
- f"๐Ÿ“‹ **ุงู„ูุฆุฉ | Category:** {category_labels.get(category, category)}\n"
472
- f"โฐ **ูˆููุชุญ ููŠ | Opened at:** <t:{int(opened_at.timestamp())}:F>"
473
- ),
474
- color=discord.Color.blue(),
475
- timestamp=opened_at,
476
- )
477
- welcome_embed.set_footer(text="Your ticket has been created, please wait... | ุชุฐูƒุฑุชูƒ ุฌุงู‡ุฒุฉุŒ ูŠูุฑุฌู‰ ุงู„ุงู†ุชุธุงุฑ...")
478
- welcome_embed.set_thumbnail(url=guild.icon.url if guild.icon else None)
479
-
480
- # ุจู†ุงุก Ping ู„ู„ู…ูˆุธููŠู†
481
- staff_pings = " ".join(f"<@&{rid}>" for rid in STAFF_ROLE_PING_IDS)
482
-
483
- # ุฅุฑุณุงู„ ุฑุณุงู„ุฉ ุงู„ุชุฑุญูŠุจ + Ping ุงู„ู…ูˆุธููŠู† + ุฃุฒุฑุงุฑ ุงู„ุชุญูƒู…
484
- control_view = TicketControlView()
485
- await thread.send(
486
- content=f"**{staff_pings}**",
487
- embed=welcome_embed,
488
- view=control_view,
489
- )
490
-
491
- # โ”€โ”€ 10. DM ุชุฃูƒูŠุฏ ู„ู„ู…ุณุชุฎุฏู… โ”€โ”€
492
- dm_embed = discord.Embed(
493
- title="โœ… ุชู… ุฅู†ุดุงุก ุชุฐูƒุฑุชูƒ | Your Ticket Has Been Created",
494
  description=(
495
- f"๐Ÿ‡ธ๐Ÿ‡ฆ **ุชู… ุฅู†ุดุงุก ุชุฐูƒุฑุชูƒ ุจู†ุฌุงุญ ููŠ ุณูŠุฑูุฑ {guild.name}!**\n"
496
- f"ูŠู…ูƒู†ูƒ ุงู„ูˆุตูˆู„ ุฅู„ูŠู‡ุง ุนุจุฑ: <#{thread.id}>\n\n"
497
- f"๐Ÿ‡ฌ๐Ÿ‡ง **Your ticket has been successfully created in {guild.name}!**\n"
498
- f"You can access it via: <#{thread.id}>\n\n"
499
- f"๐Ÿ“‹ **ุงู„ุงุณู… | Name:** `{ticket_name}`\n"
500
- f"๐Ÿ“Œ **ุงู„ูุฆุฉ | Category:** {category_labels.get(category, category)}\n"
501
- f"โฐ **ุงู„ูˆู‚ุช | Time:** <t:{int(opened_at.timestamp())}:F>"
502
- ),
503
- color=discord.Color.green(),
504
- timestamp=opened_at,
505
- )
506
- dm_embed.set_footer(text=f"{guild.name} | Ticket System")
507
- try:
508
- await user.send(embed=dm_embed)
509
- except discord.Forbidden:
510
- pass # DM ู…ุบู„ู‚
511
-
512
- # โ”€โ”€ 11. ุชุญุฏูŠุซ ุฑุณุงู„ุฉ ุงู„ุชุฃูƒูŠุฏ โ”€โ”€
513
- await interaction.followup.send(
514
- embed=discord.Embed(
515
- title="โœ… ุชู… ุฅู†ุดุงุก ุงู„ุชุฐูƒุฑุฉ | Ticket Created",
516
- description=(
517
- f"๐Ÿ‡ธ๐Ÿ‡ฆ **ุชุฐูƒุฑุชูƒ ุฌุงู‡ุฒุฉ:** <#{thread.id}>\n"
518
- f"๐Ÿ‡ฌ๐Ÿ‡ง **Your ticket is ready:** <#{thread.id}>"
519
- ),
520
- color=discord.Color.green(),
521
  ),
522
- ephemeral=True,
523
  )
 
 
524
 
525
- log.info(f"Ticket '{ticket_name}' created by {user} ({user.id}) - Category: {category}")
 
 
526
 
527
-
528
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
529
- # CORE: CLOSE TICKET | ุฅุบู„ุงู‚ ุงู„ุชุฐูƒุฑุฉ
530
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
531
- async def close_ticket(
532
- interaction: discord.Interaction,
533
- thread: discord.Thread,
534
- reason: str,
535
- ):
536
- """ุงู„ู…ู†ุทู‚ ุงู„ุฑุฆูŠุณูŠ ู„ุฅุบู„ุงู‚ ุงู„ุชุฐูƒุฑุฉ ูˆุฅุฑุณุงู„ ุงู„ู„ูˆู‚"""
537
- thread_id = thread.id
538
- data = ticket_data.get(thread_id)
539
-
540
- if not data:
541
- await interaction.followup.send(
542
- "โŒ ุจูŠุงู†ุงุช ุงู„ุชุฐูƒุฑุฉ ุบูŠุฑ ู…ูˆุฌูˆุฏุฉ. | Ticket data not found.", ephemeral=True
543
- )
544
- return
545
-
546
- closed_at = now_utc()
547
- opened_at = data["opened_at"]
548
- duration = closed_at - opened_at
549
- user_id = data["user_id"]
550
- guild = interaction.guild
551
-
552
- # โ”€โ”€ 1. ุฅู†ุดุงุก ู…ู„ู ู†ุต ุงู„ู…ุญุงุฏุซุฉ โ”€โ”€
553
- transcript_lines = [
554
- f"โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—",
555
- f"โ•‘ TICKET TRANSCRIPT | ุณุฌู„ ุงู„ุชุฐูƒุฑุฉ โ•‘",
556
- f"โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•",
557
- f"",
558
- f"Ticket Name | ุงุณู… ุงู„ุชุฐูƒุฑุฉ : {data['ticket_name']}",
559
- f"User | ุงู„ู…ุณุชุฎุฏู… : {guild.get_member(user_id) or user_id} ({user_id})",
560
- f"Staff | ุงู„ู…ูˆุธู : {interaction.user} ({interaction.user.id})",
561
- f"Category | ุงู„ูุฆุฉ : {data['category']}",
562
- f"Opened At | ูˆู‚ุช ุงู„ูุชุญ : {opened_at.strftime('%Y-%m-%d %H:%M:%S UTC')}",
563
- f"Closed At | ูˆู‚ุช ุงู„ุฅุบู„ุงู‚ : {closed_at.strftime('%Y-%m-%d %H:%M:%S UTC')}",
564
- f"Duration | ุงู„ู…ุฏุฉ : {format_duration(duration)}",
565
- f"Close Reason | ุณุจุจ ุงู„ุฅุบู„ุงู‚ : {reason}",
566
- f"",
567
- f"โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• MESSAGES | ุงู„ุฑุณุงุฆู„ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•",
568
- f"",
569
- ]
570
-
571
- # ุฌู…ุน ุฑุณุงุฆู„ ุงู„ู€ Thread
572
- messages_log = []
573
- try:
574
- async for msg in thread.history(limit=500, oldest_first=True):
575
- if msg.author.bot:
576
- continue
577
- ts = msg.created_at.strftime("%Y-%m-%d %H:%M:%S UTC")
578
- content = msg.content or "[Attachment/Embed]"
579
- messages_log.append(f"[{ts}] {msg.author} ({msg.author.id}): {content}")
580
- except Exception as e:
581
- log.warning(f"Error fetching thread history: {e}")
582
-
583
- transcript_lines.extend(messages_log)
584
- transcript_lines.append("")
585
- transcript_lines.append("โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•")
586
- transcript_text = "\n".join(transcript_lines)
587
- transcript_bytes = transcript_text.encode("utf-8")
588
-
589
- transcript_file = discord.File(
590
- fp=__import__("io").BytesIO(transcript_bytes),
591
- filename=f"{data['ticket_name']}-transcript.txt",
592
- )
593
-
594
- # โ”€โ”€ 2. ุฅุฑุณุงู„ ุงู„ู„ูˆู‚ ุฅู„ู‰ ู‚ู†ุงุฉ ุงู„ุณุฌู„ โ”€โ”€
595
- log_channel = guild.get_channel(LOG_CHANNEL_ID)
596
- if log_channel:
597
- log_embed = discord.Embed(
598
- title="๐Ÿ“ ุชุฐูƒุฑุฉ ู…ูุบู„ู‚ุฉ | Ticket Closed",
599
- color=discord.Color.red(),
600
- timestamp=closed_at,
601
- )
602
- log_embed.add_field(
603
- name="๐ŸŽซ ุงู„ุชุฐูƒุฑุฉ | Ticket",
604
- value=f"`{data['ticket_name']}`",
605
- inline=True,
606
- )
607
- log_embed.add_field(
608
- name="๐Ÿ‘ค ุงู„ู…ุณุชุฎุฏู… | User",
609
- value=f"<@{user_id}> (`{user_id}`)",
610
- inline=True,
611
- )
612
- log_embed.add_field(
613
- name="๐Ÿ‘ฎ ุงู„ู…ูˆุธู | Staff",
614
- value=f"{interaction.user.mention} (`{interaction.user.id}`)",
615
- inline=True,
616
- )
617
- log_embed.add_field(
618
- name="โฐ ูˆู‚ุช ุงู„ูุชุญ | Opened At",
619
- value=f"<t:{int(opened_at.timestamp())}:F>",
620
- inline=True,
621
- )
622
- log_embed.add_field(
623
- name="๐Ÿ”’ ูˆู‚ุช ุงู„ุฅุบู„ุงู‚ | Closed At",
624
- value=f"<t:{int(closed_at.timestamp())}:F>",
625
- inline=True,
626
- )
627
- log_embed.add_field(
628
- name="โฑ๏ธ ุงู„ู…ุฏุฉ | Duration",
629
- value=format_duration(duration),
630
- inline=True,
631
- )
632
- log_embed.add_field(
633
- name="๐Ÿ“Œ ุงู„ูุฆุฉ | Category",
634
- value=data["category"],
635
- inline=True,
636
- )
637
- log_embed.add_field(
638
- name="๐Ÿ“ ุณุจุจ ุงู„ุฅุบู„ุงู‚ | Close Reason",
639
- value=reason,
640
- inline=False,
641
- )
642
- log_embed.set_footer(text="Ticket System | ู†ุธุงู… ุงู„ุชุฐุงูƒุฑ")
643
-
644
- try:
645
- await log_channel.send(embed=log_embed, file=transcript_file)
646
- except Exception as e:
647
- log.error(f"Error sending log: {e}")
648
-
649
- # โ”€โ”€ 3. ุฅุดุนุงุฑ ุงู„ู…ุณุชุฎุฏู… ุจุงู„ุฅุบู„ุงู‚ โ”€โ”€
650
- member = guild.get_member(user_id)
651
- if member:
652
- close_dm = discord.Embed(
653
- title="๐Ÿ”’ ุชู… ุฅุบู„ุงู‚ ุชุฐูƒุฑุชูƒ | Your Ticket Has Been Closed",
654
- description=(
655
- f"๐Ÿ‡ธ๐Ÿ‡ฆ **ุชู… ุฅุบู„ุงู‚ ุชุฐูƒุฑุชูƒ `{data['ticket_name']}` ู…ู† ู‚ูุจู„:** {interaction.user.display_name}\n"
656
- f"**ุงู„ุณุจุจ:** {reason}\n\n"
657
- f"๐Ÿ‡ฌ๐Ÿ‡ง **Your ticket `{data['ticket_name']}` has been closed by:** {interaction.user.display_name}\n"
658
- f"**Reason:** {reason}"
659
- ),
660
- color=discord.Color.red(),
661
- timestamp=closed_at,
662
- )
663
- close_dm.add_field(name="โฑ๏ธ ู…ุฏุฉ ุงู„ุชุฐูƒุฑุฉ | Duration", value=format_duration(duration))
664
- close_dm.set_footer(text="Thank you for contacting us | ุดูƒุฑุงู‹ ู„ุชูˆุงุตู„ูƒ ู…ุนู†ุง")
665
- try:
666
- await member.send(embed=close_dm)
667
- except discord.Forbidden:
668
- pass
669
-
670
- # โ”€โ”€ 4. ุฅุฑุณุงู„ ุฑุณุงู„ุฉ ุฅุบู„ุงู‚ ุฏุงุฎู„ ุงู„ู€ Thread โ”€โ”€
671
- close_embed = discord.Embed(
672
- title="๐Ÿ”’ ุชู… ุฅุบู„ุงู‚ ุงู„ุชุฐูƒุฑุฉ | Ticket Closed",
673
- description=(
674
- f"๐Ÿ‡ธ๐Ÿ‡ฆ **ุชู… ุฅุบู„ุงู‚ ู‡ุฐู‡ ุงู„ุชุฐูƒุฑุฉ ู…ู† ู‚ูุจู„:** {interaction.user.mention}\n"
675
- f"**ุงู„ุณุจุจ:** {reason}\n\n"
676
- f"๐Ÿ‡ฌ๐Ÿ‡ง **This ticket has been closed by:** {interaction.user.mention}\n"
677
- f"**Reason:** {reason}"
678
- ),
679
- color=discord.Color.red(),
680
- timestamp=closed_at,
681
- )
682
- close_embed.add_field(name="โฑ๏ธ ู…ุฏุฉ ุงู„ุชุฐูƒุฑุฉ | Duration", value=format_duration(duration))
683
- try:
684
- await thread.send(embed=close_embed)
685
- except Exception:
686
- pass
687
-
688
- # โ”€โ”€ 5. ุชู†ุธูŠู ุงู„ุจูŠุงู†ุงุช ูˆุถุจุท Cooldown โ”€โ”€
689
- if user_id in active_tickets:
690
- del active_tickets[user_id]
691
- if thread_id in ticket_data:
692
- del ticket_data[thread_id]
693
-
694
- # ุถุจุท ุงู„ู€ Cooldown
695
- cooldowns[user_id] = now_utc() + datetime.timedelta(seconds=COOLDOWN_SECONDS)
696
-
697
- # โ”€โ”€ 6. ุฃุฑุดูุฉ ุงู„ู€ Thread โ”€โ”€
698
- try:
699
- await thread.edit(archived=True, locked=True)
700
- except Exception as e:
701
- log.warning(f"Error archiving thread: {e}")
702
-
703
- await interaction.followup.send(
704
- "โœ… **ุชู… ุฅุบู„ุงู‚ ุงู„ุชุฐูƒุฑุฉ ุจู†ุฌุงุญ. | Ticket closed successfully.**",
705
- ephemeral=True,
706
- )
707
-
708
- log.info(f"Ticket '{data['ticket_name']}' closed by {interaction.user} - Reason: {reason}")
709
-
710
-
711
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€๏ฟฝ๏ฟฝ๏ฟฝโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
712
- # ANTI-SPAM: MESSAGE FILTER | ูู„ุชุฑ ุงู„ุฑุณุงุฆู„
713
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
714
  @bot.event
715
- async def on_message(message: discord.Message):
716
- """ูู„ุชุฑ ุงู„ุฑุณุงุฆู„ ุฏุงุฎู„ ุงู„ุชุฐุงูƒุฑ"""
717
- if message.author.bot:
718
- return
719
-
720
- # ุงู„ุชุญู‚ู‚ ู‡ู„ ุงู„ุฑุณุงู„ุฉ ุฏุงุฎู„ Thread ุชุฐูƒุฑุฉ
721
- if not isinstance(message.channel, discord.Thread):
722
- await bot.process_commands(message)
723
- return
724
-
725
- thread_id = message.channel.id
726
- if thread_id not in ticket_data:
727
- await bot.process_commands(message)
728
- return
729
-
730
- data = ticket_data[thread_id]
731
- user_id = message.author.id
732
- content = message.content or ""
733
-
734
- # โ”€โ”€ ูู„ุชุฑ ุงู„ู…ุญุชูˆู‰ ุงู„ู…ู…ู†ูˆุน โ”€โ”€
735
-
736
- # 1. ุงู„ุฑูˆุงุจุท
737
- if contains_link(content):
738
- try:
739
- await message.delete()
740
- await message.channel.send(
741
- f"โ›” {message.author.mention} **ุงู„ุฑูˆุงุจุท ู…ู…ู†ูˆุนุฉ ุฏุงุฎู„ ุงู„ุชุฐุงูƒุฑ. | Links are not allowed in tickets.**",
742
- delete_after=5,
743
- )
744
- except Exception:
745
- pass
746
- return
747
-
748
- # 2. ุงู„ู…ู„ูุงุช ูˆุงู„ู…ุฑูู‚ุงุช ุบูŠุฑ ุงู„ู…ุณู…ูˆุญุฉ
749
- for attachment in message.attachments:
750
- fname = attachment.filename.lower()
751
- # ุงู„ุณู…ุงุญ ุจุงู„ุตูˆุฑ ูˆุงู„ููŠุฏูŠูˆ ูู‚ุท
752
- allowed_extensions = (".png", ".jpg", ".jpeg", ".gif", ".webp", ".mp4", ".mov", ".mkv", ".avi")
753
- if not fname.endswith(allowed_extensions):
754
- try:
755
- await message.delete()
756
- await message.channel.send(
757
- f"โ›” {message.author.mention} **ู‡ุฐุง ุงู„ู†ูˆุน ู…ู† ุงู„ู…ู„ูุงุช ู…ู…ู†ูˆุน. | This file type is not allowed.**\n"
758
- f"โœ… ุงู„ู…ุณู…ูˆุญ: ุตูˆุฑ ูˆููŠุฏูŠูˆ ูู‚ุท | Allowed: Images and videos only",
759
- delete_after=6,
760
- )
761
- except Exception:
762
- pass
763
- return
764
-
765
- # 3. Voice Messages
766
- if message.flags.voice:
767
- try:
768
- await message.delete()
769
- await message.channel.send(
770
- f"โ›” {message.author.mention} **ุงู„ุฑุณุงุฆู„ ุงู„ุตูˆุชูŠุฉ ู…ู…ู†ูˆุนุฉ. | Voice messages are not allowed.**",
771
- delete_after=5,
772
- )
773
- except Exception:
774
- pass
775
- return
776
-
777
- # 4. ุณุจุงู… ุงู„ุฅูŠู…ูˆุฌูŠ (ุฃูƒุซุฑ ู…ู† 10 ุฅูŠู…ูˆุฌูŠ)
778
- if count_emojis(content) > 10:
779
- try:
780
- await message.delete()
781
- await message.channel.send(
782
- f"โ›” {message.author.mention} **ุณุจุงู… ุงู„ุฅูŠู…ูˆุฌูŠ ู…ู…ู†ูˆุน. | Emoji spam is not allowed.**",
783
- delete_after=5,
784
- )
785
- except Exception:
786
- pass
787
- return
788
-
789
- # 5. ุณุจุงู… ุงู„ุญุฑูˆู ุงู„ุนุดูˆุงุฆูŠุฉ
790
- if is_random_chars(content) and len(content) > 15:
791
- try:
792
- await message.delete()
793
- await message.channel.send(
794
- f"โ›” {message.author.mention} **ุงู„ุญุฑูˆู ุงู„ุนุดูˆุงุฆูŠุฉ ู…ู…ู†ูˆุนุฉ. | Random character spam is not allowed.**",
795
- delete_after=5,
796
- )
797
- except Exception:
798
- pass
799
- return
800
-
801
- # 6. ุณุจุงู… ุงู„ุฑุณุงุฆู„ (ุณุฑุนุฉ ุงู„ุฅุฑุณุงู„)
802
- if is_spam(user_id):
803
- try:
804
  await message.delete()
805
- await message.channel.send(
806
- f"โ›” {message.author.mention} **ุฃู†ุช ุชุฑุณู„ ุฑุณุงุฆู„ ุจุณุฑุนุฉ ูƒุจูŠุฑุฉ! | You are sending messages too fast!**",
807
- delete_after=5,
808
- )
809
- except Exception:
810
- pass
811
- return
812
-
813
- # ุญูุธ ุงู„ุฑุณุงู„ุฉ ููŠ ุงู„ุณุฌู„
814
- ticket_data[thread_id]["messages"].append({
815
- "author": str(message.author),
816
- "author_id": message.author.id,
817
- "content": content,
818
- "timestamp": now_utc().isoformat(),
819
- })
820
-
821
- await bot.process_commands(message)
822
-
823
-
824
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
825
- # SLASH COMMANDS | ุฃูˆุงู…ุฑ Slash
826
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
827
- @bot.tree.command(name="setup-ticket-panel", description="ุฅุนุฏุงุฏ ู„ูˆุญุฉ ุงู„ุชุฐุงูƒุฑ | Setup ticket panel (Owner only)")
828
- async def setup_ticket_panel(interaction: discord.Interaction):
829
- """ุฃู…ุฑ ุฅุนุฏุงุฏ Panel ุงู„ุชุฐุงูƒุฑ - ู„ู„ู…ุงู„ูƒ ูู‚ุท"""
830
- if not is_owner(interaction.user):
831
- await interaction.response.send_message(
832
- "โŒ **ู‡ุฐุง ุงู„ุฃู…ุฑ ู„ู„ู…ุงู„ูƒ ูู‚ุท. | This command is for the owner only.**",
833
- ephemeral=True,
834
- )
835
- return
836
-
837
- panel_embed = discord.Embed(
838
- title="๐ŸŽซ ู†ุธุงู… ุงู„ุชุฐุงูƒุฑ | Ticket System",
839
- description=(
840
- "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n"
841
- "๐Ÿ“‹ **ู‚ูˆุงู†ูŠู† ูุชุญ ุงู„ุชุฐูƒุฑุฉ | Ticket Rules**\n"
842
- "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n\n"
843
- "โŒ **ู…ู…ู†ูˆุน | Prohibited:**\n"
844
- "โ€ข ูุชุญ ุชุฐูƒุฑุฉ ู„ุฃุณุจุงุจ ุบูŠุฑ ู…ู‡ู…ุฉ ุฃูˆ ุชุงูู‡ุฉ\n"
845
- "โ€ข Opening tickets for unimportant or trivial reasons\n"
846
- "โ€ข ุฅุฑุณุงู„ ุฑูˆุงุจุท ุฃูˆ ู…ู„ูุงุช ุฏุงุฎู„ ุงู„ุชุฐูƒุฑุฉ\n"
847
- "โ€ข Sending links or files inside tickets\n"
848
- "โ€ข ุงู„ุณุจุงู… ุฃูˆ ุงู„ุฑุณุงุฆู„ ุงู„ุนุดูˆุงุฆูŠุฉ\n"
849
- "โ€ข Spam or random messages\n\n"
850
- "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n"
851
- "โœ… **ุงู„ุฃุณุจุงุจ ุงู„ู…ุณู…ูˆุญ ุจู‡ุง | Allowed Reasons:**\n"
852
- "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n\n"
853
- "๐Ÿ‘ฎ **ุดูƒูˆู‰ ุถุฏ ู…ูˆุธู | Staff Complaint**\n"
854
- " โ† ุฅุฐุง ูƒุงู† ู…ูˆุธู ุชุตุฑู ุจุดูƒู„ ุฎุงุทุฆ\n"
855
- " โ† If a staff member acted inappropriately\n\n"
856
- "๐Ÿšจ **ุดูƒูˆู‰ ุถุฏ ู…ุณุชุฎุฏู… | User Complaint**\n"
857
- " โ† ุฅุฐุง ูƒุงู† ู…ุณุชุฎุฏู… ูŠู†ุชู‡ูƒ ุงู„ู‚ูˆุงู†ูŠู†\n"
858
- " โ† If a user is violating rules\n\n"
859
- "๐Ÿ› **ุงู„ุฅุจู„ุงุบ ุนู† ุฎุทุฃ | Bug Report**\n"
860
- " โ† ู…ุดูƒู„ุฉ ุชู‚ู†ูŠุฉ ููŠ ุงู„ุณูŠุฑูุฑ ุฃูˆ ุงู„ุจูˆุช\n"
861
- " โ† Technical issue with the server or bot\n\n"
862
- "๐Ÿ”ง **ุงู„ุฏุนู… ุงู„ุชู‚ู†ูŠ | Technical Support**\n"
863
- " โ† ุทู„ุจ ุงู„ู…ุณุงุนุฏุฉ ููŠ ู…ุดูƒู„ุฉ ุชู‚ู†ูŠุฉ\n"
864
- " โ† Requesting help with a technical issue\n\n"
865
- "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n"
866
- "โฌ‡๏ธ **ุงุฎุชุฑ ู†ูˆุน ุชุฐูƒุฑุชูƒ ู…ู† ุงู„ู‚ุงุฆู…ุฉ ุฃุฏู†ุงู‡:**\n"
867
- "โฌ‡๏ธ **Choose your ticket type from the menu below:**"
868
- ),
869
- color=discord.Color.from_rgb(88, 101, 242),
870
- timestamp=now_utc(),
871
- )
872
- panel_embed.set_footer(
873
- text="Ticket System | ู†ุธุงู… ุงู„ุชุฐุงูƒุฑ โ€ข ู„ุง ุชูุชุญ ุชุฐูƒุฑุฉ ุฅู„ุง ุนู†ุฏ ุงู„ุญุงุฌุฉ ุงู„ูุนู„ูŠุฉ"
874
- )
875
- if interaction.guild.icon:
876
- panel_embed.set_thumbnail(url=interaction.guild.icon.url)
877
-
878
- view = TicketPanelView()
879
- await interaction.response.send_message(embed=panel_embed, view=view)
880
- log.info(f"Ticket panel set up by {interaction.user} ({interaction.user.id}) in #{interaction.channel.name}")
881
-
882
-
883
- @bot.tree.command(name="help", description="ูƒูŠููŠุฉ ูุชุญ ุชุฐูƒุฑุฉ | How to open a ticket")
884
- async def help_command(interaction: discord.Interaction):
885
- """ุฃู…ุฑ ุงู„ู…ุณุงุนุฏุฉ"""
886
- await interaction.response.send_message(
887
- embed=discord.Embed(
888
- title="๐Ÿ“– ุงู„ู…ุณุงุนุฏุฉ | Help",
889
- description=(
890
- f"๐Ÿ‡ธ๐Ÿ‡ฆ **ู„ูุชุญ ุชุฐูƒุฑุฉุŒ ุงุฐู‡ุจ ุฅู„ู‰:** <#{TICKET_CHANNEL_ID}>\n\n"
891
- f"๐Ÿ‡ฌ๐Ÿ‡ง **To open a ticket, go to:** <#{TICKET_CHANNEL_ID}>"
892
- ),
893
- color=discord.Color.blue(),
894
- timestamp=now_utc(),
895
- ),
896
- ephemeral=True,
897
- )
898
-
899
-
900
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
901
- # BOT EVENTS | ุฃุญุฏุงุซ ุงู„ุจูˆุช
902
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
903
- @bot.event
904
- async def on_ready():
905
- """ุนู†ุฏ ุชุดุบูŠู„ ุงู„ุจูˆุช"""
906
- log.info(f"โœ… Bot logged in as {bot.user} ({bot.user.id})")
907
- log.info(f"โœ… Connected to {len(bot.guilds)} guild(s)")
908
-
909
- # ุฅุนุงุฏุฉ ุชุณุฌูŠู„ ุงู„ู€ Views ุงู„ุซุงุจุชุฉ ู„ุชุนู…ู„ ุจุนุฏ ุฅุนุงุฏุฉ ุงู„ุชุดุบูŠู„
910
- bot.add_view(TicketPanelView())
911
- bot.add_view(TicketControlView())
912
-
913
- # ู…ุฒุงู…ู†ุฉ Slash Commands
914
- try:
915
- synced = await bot.tree.sync()
916
- log.info(f"โœ… Synced {len(synced)} slash command(s)")
917
- except Exception as e:
918
- log.error(f"โŒ Failed to sync commands: {e}")
919
-
920
- # ุชุบูŠูŠุฑ ุงู„ู€ Status
921
- await bot.change_presence(
922
- activity=discord.Activity(
923
- type=discord.ActivityType.watching,
924
- name="๐ŸŽซ Ticket System | ู†ุธุงู… ุงู„ุชุฐุงูƒุฑ",
925
- ),
926
- status=discord.Status.online,
927
- )
928
-
929
-
930
- @bot.event
931
- async def on_error(event: str, *args, **kwargs):
932
- """ู…ุนุงู„ุฌุฉ ุงู„ุฃุฎุทุงุก ุงู„ุนุงู…ุฉ"""
933
- error_text = traceback.format_exc()
934
- log.error(f"Error in event '{event}': {error_text}")
935
- await send_error_to_channel(f"**Event Error: `{event}`**\n```\n{error_text[:1800]}\n```")
936
-
937
-
938
- @bot.tree.error
939
- async def on_app_command_error(interaction: discord.Interaction, error: app_commands.AppCommandError):
940
- """ู…ุนุงู„ุฌุฉ ุฃุฎุทุงุก Slash Commands"""
941
- error_msg = str(error)
942
- log.error(f"Slash command error: {error_msg}")
943
-
944
- if not interaction.response.is_done():
945
- await interaction.response.send_message(
946
- f"โŒ **ุญุฏุซ ุฎุทุฃ. | An error occurred.**\n```{error_msg[:500]}```",
947
- ephemeral=True,
948
- )
949
- else:
950
- await interaction.followup.send(
951
- f"โŒ **ุญุฏุซ ุฎุทุฃ. | An error occurred.**\n```{error_msg[:500]}```",
952
- ephemeral=True,
953
- )
954
-
955
- await send_error_to_channel(
956
- f"**Slash Command Error**\n"
957
- f"User: {interaction.user} (`{interaction.user.id}`)\n"
958
- f"Command: `/{interaction.command.name if interaction.command else 'unknown'}`\n"
959
- f"```\n{error_msg[:1500]}\n```"
960
- )
961
-
962
-
963
- async def send_error_to_channel(message: str):
964
- """ุฅุฑุณุงู„ ุงู„ุฃุฎุทุงุก ุฅู„ู‰ ู‚ู†ุงุฉ ุงู„ุฃุฎุทุงุก ู…ุน Ping ุงู„ู…ุงู„ูƒ"""
965
- try:
966
- for guild in bot.guilds:
967
- error_channel = guild.get_channel(ERROR_CHANNEL_ID)
968
- if error_channel:
969
- await error_channel.send(
970
- f"<@{OWNER_ID}> ๐Ÿšจ **ุญุฏุซ ุฎุทุฃ ููŠ ุงู„ุจูˆุช! | Bot Error!**\n{message[:1900]}"
971
- )
972
- break
973
- except Exception as e:
974
- log.error(f"Could not send error to channel: {e}")
975
 
 
 
 
 
976
 
977
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
978
- # ANTI-CRASH SYSTEM | ู†ุธุงู… ู…ู†ุน ุงู„ุงู†ู‡ูŠุงุฑ
979
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
980
- async def main():
981
- """ุงู„ุฏุงู„ุฉ ุงู„ุฑุฆูŠุณูŠุฉ ู…ุน ู†ุธุงู… ุฅุนุงุฏุฉ ุงู„ุชุดุบูŠู„ ุงู„ุชู„ู‚ุงุฆูŠ"""
982
- if not TOKEN:
983
- log.critical("โŒ TOKEN not found! Set TOKEN environment variable.")
984
- sys.exit(1)
985
-
986
- retry_delay = 5 # ุซูˆุงู†ูŠ ู‚ุจู„ ุฅุนุงุฏุฉ ุงู„ุงุชุตุงู„
987
-
988
- while True:
989
- try:
990
- log.info("๐Ÿš€ Starting bot...")
991
- async with bot:
992
- await bot.start(TOKEN)
993
- except discord.LoginFailure:
994
- log.critical("โŒ Invalid TOKEN. Please check your token.")
995
- sys.exit(1)
996
- except discord.HTTPException as e:
997
- log.error(f"HTTP error: {e}. Retrying in {retry_delay}s...")
998
- await asyncio.sleep(retry_delay)
999
- except Exception as e:
1000
- error_text = traceback.format_exc()
1001
- log.error(f"Unexpected crash: {error_text}")
1002
- # ู…ุญุงูˆู„ุฉ ุฅุฑุณุงู„ ุงู„ุฎุทุฃ
1003
- try:
1004
- async with bot:
1005
- await send_error_to_channel(
1006
- f"**CRASH DETECTED! | ุชู… ุงูƒุชุดุงู ุงู†ู‡ูŠุงุฑ!**\n```\n{error_text[:1500]}\n```"
1007
- )
1008
- except Exception:
1009
- pass
1010
- log.info(f"๐Ÿ”„ Restarting in {retry_delay} seconds...")
1011
- await asyncio.sleep(retry_delay)
1012
- retry_delay = min(retry_delay * 2, 60) # ุฒูŠุงุฏุฉ ุงู„ุชุฃุฎูŠุฑ ุชุฏุฑูŠุฌูŠุงู‹ ุญุชู‰ 60 ุซุงู†ูŠุฉ
1013
- else:
1014
- break
1015
-
1016
-
1017
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1018
- # ENTRY POINT | ู†ู‚ุทุฉ ุงู„ุฏุฎูˆู„
1019
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1020
- if __name__ == "__main__":
1021
- try:
1022
- asyncio.run(main())
1023
- except KeyboardInterrupt:
1024
- log.info("๐Ÿ‘‹ Bot stopped by user.")
 
 
 
 
 
 
 
 
 
 
 
 
1
  import discord
 
2
  from discord import app_commands
3
+ from discord.ext import commands
4
+ import datetime
 
 
 
 
5
  import random
6
  import string
7
+ import io
8
+ import asyncio
9
  import re
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ # --- ุงู„ุฅุนุฏุงุฏุงุช ุงู„ุซุงุจุชุฉ ---
12
  OWNER_ID = 1429183440485486679
13
+ ERROR_LOG_CHANNEL_ID = 1488536752691085552
14
+ TICKETS_CHANNEL_ID = 1488536530019549344
15
+ LOG_CHANNEL_ID = 1488536921813680218
16
+ STAFF_ROLES_IDS = [
17
+ 1488187501142216826, 1488187641424773140, 1488187816201687181,
18
+ 1488188612313874733, 1488537308700344490, 1488537566910218260
 
 
 
 
19
  ]
20
+ PING_ROLES = "<@&1488187816201687181> <@&1488188612313874733> <@&1488537308700344490> <@&1488537566910218260>"
21
 
22
+ # --- ู†ุธุงู… ุงู„ุญู…ุงูŠุฉ ูˆุงู„ู‚ูŠูˆุฏ ---
23
+ active_tickets = {} # user_id: thread_id
24
+ cooldowns = {} # user_id: datetime
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ class MyBot(commands.Bot):
27
+ def __init__(self):
28
+ intents = discord.Intents.default()
29
+ intents.message_content = True
30
+ intents.members = True
31
+ super().__init__(command_prefix="!", intents=intents)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
+ async def setup_hook(self):
34
+ self.add_view(TicketPanelView())
35
+ self.add_view(TicketControls())
36
+ await self.tree.sync()
37
 
38
+ async def on_error(self, event, *args, **kwargs):
39
+ channel = self.get_channel(ERROR_LOG_CHANNEL_ID)
40
+ if channel:
41
+ await channel.send(f"โš ๏ธ **Crash Detected / ุฎุทุฃ ููŠ ุงู„ู†ุธุงู…**\n`{event}`\n<@{OWNER_ID}>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ bot = MyBot()
 
44
 
45
+ # --- ุฃุฏูˆุงุช ู…ุณุงุนุฏุฉ ---
46
+ def is_staff(interaction: discord.Interaction):
47
+ return any(role.id in STAFF_ROLES_IDS for role in interaction.user.roles) or interaction.user.id == OWNER_ID
48
 
49
+ # --- ู†ุธุงู… ูˆุงุฌู‡ุฉ ุงู„ุชุฐุงูƒุฑ ---
50
  class TicketPanelView(discord.ui.View):
 
 
51
  def __init__(self):
52
  super().__init__(timeout=None)
 
53
 
54
+ @discord.ui.select(
55
+ custom_id="ticket_select",
56
+ placeholder="Choose ticket type / ุงุฎุชุฑ ู†ูˆุน ุงู„ุชุฐูƒุฑุฉ",
57
+ options=[
58
+ discord.SelectOption(label="Complaint against staff / ุดูƒูˆู‰ ุถุฏ ุฅุฏุงุฑูŠ", value="staff_complaint", emoji="โš–๏ธ"),
59
+ discord.SelectOption(label="Complaint against user / ุดูƒูˆู‰ ุถุฏ ู„ุงุนุจ", value="user_complaint", emoji="๐Ÿ‘ฅ"),
60
+ discord.SelectOption(label="Bug report / ุจู„ุงุบ ุนู† ุซุบุฑุฉ", value="bug_report", emoji="๐Ÿ›"),
61
+ discord.SelectOption(label="Technical support / ุฏุนู… ูู†ูŠ", value="tech_support", emoji="๐Ÿ› ๏ธ"),
62
+ ]
63
+ )
64
+ async def select_callback(self, interaction: discord.Interaction, select: discord.ui.Select):
65
+ user_id = interaction.user.id
66
+
67
+ # ุชุญู‚ู‚ ู…ู† ุงู„ุชุฐูƒุฑุฉ ุงู„ู…ูุชูˆุญุฉ
68
+ if user_id in active_tickets:
69
+ return await interaction.response.send_message("โŒ You already have an open ticket! / ู„ุฏูŠูƒ ุชุฐูƒุฑุฉ ู…ูุชูˆุญุฉ ุจุงู„ูุนู„!", ephemeral=True)
70
+
71
+ # ุชุญู‚ู‚ ู…ู† ุงู„ู€ Cooldown
72
+ if user_id in cooldowns:
73
+ diff = (datetime.datetime.now() - cooldowns[user_id]).total_seconds()
74
+ if diff < 60:
75
+ return await interaction.response.send_message(f"โณ Wait {int(60-diff)}s / ุงู†ุชุธุฑ {int(60-diff)} ุซุงู†ูŠุฉ", ephemeral=True)
76
 
77
+ await interaction.response.defer(ephemeral=True)
78
+
79
+ # ุฅู†ุดุงุก Thread
80
+ channel = bot.get_channel(TICKETS_CHANNEL_ID)
81
+ ticket_id = ''.join(random.choices(string.ascii_lowercase + string.digits, k=4))
82
+ thread_name = f"ticket-{random.randint(1000,9999)}-{ticket_id}"
83
+
84
+ thread = await channel.create_thread(
85
+ name=thread_name,
86
+ type=discord.ChannelType.private_thread,
87
+ invitable=False
88
+ )
89
+
90
+ active_tickets[user_id] = thread.id
91
+ await thread.add_user(interaction.user)
92
+ await thread.edit(slowmode_delay=10)
93
+
94
+ # ุฑุณุงู„ุฉ ุงู„ุชุฑุญูŠุจ
95
+ embed = discord.Embed(
96
+ title="Ticket Created / ุชู… ุฅู†ุดุงุก ุงู„ุชุฐูƒุฑุฉ",
97
+ description=f"Welcome {interaction.user.mention}\n{PING_ROLES}\nYour ticket has been created, please wait...\n\nู…ุฑุญุจุงู‹ ุจูƒุŒ ุชู… ุฅู†ุดุงุก ุชุฐูƒุฑุชูƒุŒ ูŠุฑุฌู‰ ุงู„ุงู†ุชุธุงุฑ...",
98
+ color=discord.Color.blue()
99
+ )
100
+ await thread.send(embed=embed, view=TicketControls())
101
+
102
+ # ุฅุฑุณุงู„ DM
103
+ try:
104
+ await interaction.user.send(f"โœ… Your ticket has been created: {thread.jump_url}\nุชู… ุฅู†ุดุงุก ุชุฐูƒุฑุชูƒ ุจู†ุฌุงุญ.")
105
+ except: pass
106
+
107
+ await interaction.followup.send(f"โœ… Ticket Created: {thread.mention}", ephemeral=True)
108
 
109
+ class TicketControls(discord.ui.View):
110
  def __init__(self):
111
  super().__init__(timeout=None)
112
 
113
+ @discord.ui.button(label="Claim / ุงุณุชู„ุงู…", style=discord.ButtonStyle.green, custom_id="claim_btn")
114
+ async def claim(self, interaction: discord.Interaction, button: discord.ui.Button):
115
+ if not is_staff(interaction):
116
+ return await interaction.response.send_message("โŒ Staff only / ู„ู„ู…ูˆุธููŠู† ูู‚ุท", ephemeral=True)
117
+
118
+ button.disabled = True
119
+ await interaction.response.edit_message(view=self)
120
+ await interaction.followup.send(f"๐Ÿ“Œ Ticket claimed by / ุชู… ุงุณุชู„ุงู… ุงู„ุชุฐูƒุฑุฉ ู…ู† ู‚ุจู„: {interaction.user.mention}")
121
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  # ุฅุฑุณุงู„ DM ู„ู„ู…ุณุชุฎุฏู…
123
+ thread_owner_id = next((k for k, v in active_tickets.items() if v == interaction.channel_id), None)
124
+ if thread_owner_id:
125
+ user = await bot.fetch_user(thread_owner_id)
126
+ try: await user.send(f"๐Ÿ‘‹ Your ticket has been claimed by **{interaction.user.name}**\nุชู… ุงุณุชู„ุงู… ุชุฐูƒุฑุชูƒ ุจูˆุงุณุทุฉ ุงู„ู…ูˆุธู.")
127
+ except: pass
128
+
129
+ @discord.ui.button(label="Close / ุฅุบู„ุงู‚", style=discord.ButtonStyle.red, custom_id="close_btn")
130
+ async def close(self, interaction: discord.Interaction, button: discord.ui.Button):
131
+ if not is_staff(interaction):
132
+ return await interaction.response.send_message("โŒ Staff only / ู„ู„ู…ูˆุธููŠู† ูู‚ุท", ephemeral=True)
133
+
134
+ await interaction.response.send_modal(CloseModal())
135
+
136
+ class CloseModal(discord.ui.Modal, title="Close Ticket / ุฅุบู„ุงู‚ ุงู„ุชุฐูƒุฑุฉ"):
137
+ reason = discord.ui.TextInput(label="Reason / ุงู„ุณุจุจ", placeholder="Enter closing reason...", min_length=5, required=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  async def on_submit(self, interaction: discord.Interaction):
140
+ thread = interaction.channel
141
+ messages = [msg async for msg in thread.history(limit=None, oldest_first=True)]
142
+
143
+ # ุฅู†ุดุงุก ู…ู„ู ุณุฌู„
144
+ transcript = ""
145
+ for m in messages:
146
+ transcript += f"[{m.created_at.strftime('%Y-%m-%d %H:%M')}] {m.author}: {m.content}\n"
147
+
148
+ file = discord.File(io.BytesIO(transcript.encode()), filename=f"{thread.name}.txt")
149
+
150
+ # ุฅุฑุณุงู„ ุงู„ู„ูˆู‚
151
+ log_channel = bot.get_channel(LOG_CHANNEL_ID)
152
+ user_id = next((k for k, v in active_tickets.items() if v == thread.id), "Unknown")
153
+
154
+ embed = discord.Embed(title="Ticket Closed / ุฅุบู„ุงู‚ ุชุฐูƒุฑุฉ", color=discord.Color.red())
155
+ embed.add_field(name="User / ุงู„ู…ุณุชุฎุฏู…", value=f"<@{user_id}>")
156
+ embed.add_field(name="Staff / ุงู„ู…ูˆุธู", value=interaction.user.mention)
157
+ embed.add_field(name="Reason / ุงู„ุณุจุจ", value=self.reason.value)
158
+ embed.add_field(name="Open Time / ูˆู‚ุช ุงู„ูุชุญ", value=thread.created_at.strftime('%Y-%m-%d %H:%M'))
159
+
160
+ await log_channel.send(embed=embed, file=file)
161
+
162
+ # ุชู†ุธูŠู ุงู„ุจูŠุงู†ุงุช
163
+ if user_id != "Unknown":
164
+ active_tickets.pop(int(user_id), None)
165
+ cooldowns[int(user_id)] = datetime.datetime.now()
166
+
167
+ await interaction.response.send_message("Closing... / ุฌุงุฑู ุงู„ุฅุบู„ุงู‚...")
168
+ await thread.delete()
169
+
170
+ # --- ุงู„ุฃูˆุงู…ุฑ ---
171
+ @bot.tree.command(name="setup-ticket-panel", description="Setup the ticket panel (Owner Only)")
172
+ async def setup_ticket(interaction: discord.Interaction):
173
+ if interaction.user.id != OWNER_ID:
174
+ return await interaction.response.send_message("โŒ Access Denied", ephemeral=True)
175
+
176
+ embed = discord.Embed(
177
+ title="Support System / ู†ุธุงู… ุงู„ุฏุนู… ุงู„ูู†ูŠ",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  description=(
179
+ "**Rules / ุงู„ู‚ูˆุงู†ูŠู†:**\n"
180
+ "โŒ No spamming tickets / ูŠู…ู†ุน ูุชุญ ุชุฐุงูƒุฑ ุนุดูˆุงุฆูŠุฉ\n"
181
+ "โœ… Use for complaints or support / ุงุณุชุฎุฏู… ุงู„ุชุฐุงูƒุฑ ู„ู„ุดูƒุงูˆู‰ ูˆุงู„ุฏุนู… ูู‚ุท\n\n"
182
+ "**Options / ุงู„ุฎูŠุงุฑุงุช:**\n"
183
+ "โ€ข Staff Complaint / ุดูƒูˆู‰ ุฅุฏุงุฑูŠ\n"
184
+ "โ€ข User Complaint / ุดูƒูˆู‰ ู„ุงุนุจ\n"
185
+ "โ€ข Bug Report / ุจู„ุงุบ ุซุบุฑุฉ\n"
186
+ "โ€ข Technical Support / ุฏุนู… ูู†ูŠ"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  ),
188
+ color=discord.Color.green()
189
  )
190
+ await interaction.response.send_message("Panel Sent!", ephemeral=True)
191
+ await interaction.channel.send(embed=embed, view=TicketPanelView())
192
 
193
+ @bot.tree.command(name="help", description="Show help information")
194
+ async def help_cmd(interaction: discord.Interaction):
195
+ await interaction.response.send_message(f"To open a ticket, go to <#{TICKETS_CHANNEL_ID}>\nู„ูุชุญ ุชุฐูƒุฑุฉุŒ ุชูˆุฌู‡ ุฅู„ู‰ ุงู„ู‚ู†ุงุฉ ุงู„ู…ุฎุตุตุฉ.", ephemeral=True)
196
 
197
+ # --- ู†ุธุงู… ุงู„ุญู…ุงูŠุฉ (Anti-Spam & Content Filter) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  @bot.event
199
+ async def on_message(message):
200
+ if message.author.bot: return
201
+ if not isinstance(message.channel, discord.Thread): return
202
+ if message.channel.parent_id != TICKETS_CHANNEL_ID: return
203
+
204
+ # ู…ู†ุน ุงู„ุฑูˆุงุจุทุŒ ุงู„ู…ู„ูุงุชุŒ ูˆุงู„ุจุตู…ุงุช ุงู„ุตูˆุชูŠุฉ
205
+ if re.search(r'http[s]?://', message.content) or message.attachments or message.flags.voice:
206
+ # ุงู„ุณู…ุงุญ ูู‚ุท ุจุงู„ุตูˆุฑ ูˆุงู„ููŠุฏูŠูˆ
207
+ allowed = True
208
+ if message.attachments:
209
+ for att in message.attachments:
210
+ if not att.content_type or (not att.content_type.startswith('image/') and not att.content_type.startswith('video/')):
211
+ allowed = False
212
+
213
+ if not allowed or re.search(r'http[s]?://', message.content) or message.flags.voice:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  await message.delete()
215
+ return await message.channel.send(f"{message.author.mention} โŒ Only text, images, and videos are allowed.\nู…ุณู…ูˆุญ ูู‚ุท ุจุงู„ู†ุตูˆุตุŒ ุงู„ุตูˆุฑุŒ ูˆุงู„ููŠุฏูŠูˆ.", delete_after=5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
+ # ู…ู†ุน ุงู„ุณุจุงู… (ุฃุญุฑู ุนุดูˆุงุฆูŠุฉ ุทูˆูŠู„ุฉ)
218
+ if len(message.content) > 50 and len(set(message.content)) < 10:
219
+ await message.delete()
220
+ return await message.channel.send("โŒ Random characters/spam detected.", delete_after=5)
221
 
222
+ import os
223
+ from dotenv import load_dotenv
224
+ load_dotenv()
225
+ bot.run(os.getenv('TOKEN'))