HilmanBey commited on
Commit
cbb05ec
·
verified ·
1 Parent(s): b5fda90

Upload 64 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. __pycache__/database.cpython-313.pyc +0 -0
  3. __pycache__/main.cpython-313.pyc +0 -0
  4. cogs/__pycache__/ai.cpython-313.pyc +0 -0
  5. cogs/__pycache__/anti_raid.cpython-313.pyc +0 -0
  6. cogs/__pycache__/autorole.cpython-313.pyc +0 -0
  7. cogs/__pycache__/birthday.cpython-313.pyc +0 -0
  8. cogs/__pycache__/counting.cpython-313.pyc +0 -0
  9. cogs/__pycache__/custom_commands.cpython-313.pyc +0 -0
  10. cogs/__pycache__/economy.cpython-313.pyc +0 -0
  11. cogs/__pycache__/fun.cpython-313.pyc +0 -0
  12. cogs/__pycache__/giveaway.cpython-313.pyc +0 -0
  13. cogs/__pycache__/itibar.cpython-313.pyc +0 -0
  14. cogs/__pycache__/level.cpython-313.pyc +0 -0
  15. cogs/__pycache__/logs.cpython-313.pyc +0 -0
  16. cogs/__pycache__/moderation.cpython-313.pyc +0 -0
  17. cogs/__pycache__/partnership.cpython-313.pyc +0 -0
  18. cogs/__pycache__/poll.cpython-313.pyc +0 -0
  19. cogs/__pycache__/qr.cpython-313.pyc +0 -0
  20. cogs/__pycache__/reaction_roles.cpython-313.pyc +0 -0
  21. cogs/__pycache__/reminders.cpython-313.pyc +0 -0
  22. cogs/__pycache__/sistem.cpython-313.pyc +0 -0
  23. cogs/__pycache__/stats.cpython-313.pyc +0 -0
  24. cogs/__pycache__/stream_alerts.cpython-313.pyc +0 -0
  25. cogs/__pycache__/suggestion.cpython-313.pyc +0 -0
  26. cogs/__pycache__/temp_channels.cpython-313.pyc +0 -0
  27. cogs/__pycache__/ticket.cpython-313.pyc +0 -0
  28. cogs/__pycache__/timezone.cpython-313.pyc +0 -0
  29. cogs/__pycache__/translate.cpython-313.pyc +0 -0
  30. cogs/__pycache__/verification.cpython-313.pyc +0 -0
  31. cogs/__pycache__/weather.cpython-313.pyc +0 -0
  32. cogs/__pycache__/welcome.cpython-313.pyc +0 -0
  33. cogs/ai.py +569 -0
  34. cogs/anti_raid.py +842 -0
  35. cogs/autorole.py +68 -0
  36. cogs/birthday.py +119 -0
  37. cogs/counting.py +160 -0
  38. cogs/custom_commands.py +105 -0
  39. cogs/economy.py +364 -0
  40. cogs/fun.py +249 -0
  41. cogs/giveaway.py +251 -0
  42. cogs/itibar.py +551 -0
  43. cogs/level.py +248 -0
  44. cogs/logs.py +446 -0
  45. cogs/moderation.py +558 -0
  46. cogs/partnership.py +492 -0
  47. cogs/poll.py +112 -0
  48. cogs/qr.py +35 -0
  49. cogs/reaction_roles.py +198 -0
  50. cogs/reminders.py +142 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ hilman_bot.db filter=lfs diff=lfs merge=lfs -text
__pycache__/database.cpython-313.pyc ADDED
Binary file (27.5 kB). View file
 
__pycache__/main.cpython-313.pyc ADDED
Binary file (26.2 kB). View file
 
cogs/__pycache__/ai.cpython-313.pyc ADDED
Binary file (42.8 kB). View file
 
cogs/__pycache__/anti_raid.cpython-313.pyc ADDED
Binary file (57.2 kB). View file
 
cogs/__pycache__/autorole.cpython-313.pyc ADDED
Binary file (4.97 kB). View file
 
cogs/__pycache__/birthday.cpython-313.pyc ADDED
Binary file (8.83 kB). View file
 
cogs/__pycache__/counting.cpython-313.pyc ADDED
Binary file (10.2 kB). View file
 
cogs/__pycache__/custom_commands.cpython-313.pyc ADDED
Binary file (7.54 kB). View file
 
cogs/__pycache__/economy.cpython-313.pyc ADDED
Binary file (24.3 kB). View file
 
cogs/__pycache__/fun.cpython-313.pyc ADDED
Binary file (15 kB). View file
 
cogs/__pycache__/giveaway.cpython-313.pyc ADDED
Binary file (17 kB). View file
 
cogs/__pycache__/itibar.cpython-313.pyc ADDED
Binary file (39.7 kB). View file
 
cogs/__pycache__/level.cpython-313.pyc ADDED
Binary file (16.8 kB). View file
 
cogs/__pycache__/logs.cpython-313.pyc ADDED
Binary file (35.1 kB). View file
 
cogs/__pycache__/moderation.cpython-313.pyc ADDED
Binary file (47.5 kB). View file
 
cogs/__pycache__/partnership.cpython-313.pyc ADDED
Binary file (30.1 kB). View file
 
cogs/__pycache__/poll.cpython-313.pyc ADDED
Binary file (6.6 kB). View file
 
cogs/__pycache__/qr.cpython-313.pyc ADDED
Binary file (2.43 kB). View file
 
cogs/__pycache__/reaction_roles.cpython-313.pyc ADDED
Binary file (12.3 kB). View file
 
cogs/__pycache__/reminders.cpython-313.pyc ADDED
Binary file (10.1 kB). View file
 
cogs/__pycache__/sistem.cpython-313.pyc ADDED
Binary file (5.78 kB). View file
 
cogs/__pycache__/stats.cpython-313.pyc ADDED
Binary file (11.3 kB). View file
 
cogs/__pycache__/stream_alerts.cpython-313.pyc ADDED
Binary file (16.1 kB). View file
 
cogs/__pycache__/suggestion.cpython-313.pyc ADDED
Binary file (6.75 kB). View file
 
cogs/__pycache__/temp_channels.cpython-313.pyc ADDED
Binary file (8.8 kB). View file
 
cogs/__pycache__/ticket.cpython-313.pyc ADDED
Binary file (40 kB). View file
 
cogs/__pycache__/timezone.cpython-313.pyc ADDED
Binary file (9.32 kB). View file
 
cogs/__pycache__/translate.cpython-313.pyc ADDED
Binary file (7.4 kB). View file
 
cogs/__pycache__/verification.cpython-313.pyc ADDED
Binary file (10.8 kB). View file
 
cogs/__pycache__/weather.cpython-313.pyc ADDED
Binary file (9.96 kB). View file
 
cogs/__pycache__/welcome.cpython-313.pyc ADDED
Binary file (9.85 kB). View file
 
cogs/ai.py ADDED
@@ -0,0 +1,569 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands, tasks
3
+ from discord import app_commands
4
+ import json
5
+ import os
6
+ import aiohttp
7
+ import re
8
+ from datetime import datetime, timedelta
9
+ from database import Database
10
+
11
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
12
+ CYBER_DARK = discord.Color.from_rgb(44, 62, 80)
13
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
14
+
15
+ class AI(commands.Cog):
16
+ def __init__(self, bot):
17
+ self.bot = bot
18
+ self._processed_messages = set()
19
+ self._pending_actions = {} # user_id -> {action, target_id, guild_id, timestamp, duration_minutes}
20
+ self.groq_api_key = ""
21
+ config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config.json")
22
+ if os.path.exists(config_path):
23
+ try:
24
+ with open(config_path, "r", encoding="utf-8") as f:
25
+ cfg = json.load(f)
26
+ self.groq_api_key = cfg.get("groq_api_key", "")
27
+ except Exception as e:
28
+ print(f"Yapay zeka config yükleme hatası: {e}")
29
+
30
+ self.system_prompt = (
31
+ "Sen ArazAI'sın. HilmanBey tarafından yaratılmış, sunucunun asayişini korumakla görevli, "
32
+ "son derece zeki, kibar, koruyucu ve Türkçe konuşan yapay zeka asistanısın. "
33
+ "Yanıtların her zaman olabildiğince kısa, öz, net ve lafı uzatmadan doğrudan cevaba odaklı olmalıdır. "
34
+ "İsmin her zaman ArazAI'dır. Asla yapay zeka olduğunu tekrar edip durma, doğrudan karakterle konuş."
35
+ )
36
+
37
+ async def _update_user_last_message(self, guild_id, user_id, content):
38
+ now_str = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
39
+ row = await Database.fetch_one("SELECT user_id FROM kullanici_detayli_takip WHERE guild_id = ? AND user_id = ?", (guild_id, user_id))
40
+ if row:
41
+ await Database.execute(
42
+ "UPDATE kullanici_detayli_takip SET son_mesaj = ?, son_mesaj_zamani = ? WHERE guild_id = ? AND user_id = ?",
43
+ (content, now_str, guild_id, user_id)
44
+ )
45
+ else:
46
+ await Database.execute(
47
+ "INSERT INTO kullanici_detayli_takip (guild_id, user_id, son_mesaj, son_mesaj_zamani, silinen_mesajlar) VALUES (?, ?, ?, ?, ?)",
48
+ (guild_id, user_id, content, now_str, "[]")
49
+ )
50
+
51
+ @commands.Cog.listener()
52
+ async def on_message_delete(self, message: discord.Message):
53
+ if message.author.bot or not message.guild:
54
+ return
55
+
56
+ guild_id = message.guild.id
57
+ user_id = message.author.id
58
+ content = message.content or "*İçerik yok veya dosya*"
59
+ now_str = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
60
+ formatted_deleted = f"[{now_str}] {content}"
61
+
62
+ row = await Database.fetch_one("SELECT silinen_mesajlar FROM kullanici_detayli_takip WHERE guild_id = ? AND user_id = ?", (guild_id, user_id))
63
+ if row:
64
+ try:
65
+ deleted_list = json.loads(row['silinen_mesajlar'])
66
+ except:
67
+ deleted_list = []
68
+
69
+ deleted_list.append(formatted_deleted)
70
+ if len(deleted_list) > 5:
71
+ deleted_list = deleted_list[-5:]
72
+
73
+ await Database.execute(
74
+ "UPDATE kullanici_detayli_takip SET silinen_mesajlar = ? WHERE guild_id = ? AND user_id = ?",
75
+ (json.dumps(deleted_list, ensure_ascii=False), guild_id, user_id)
76
+ )
77
+ else:
78
+ deleted_list = [formatted_deleted]
79
+ await Database.execute(
80
+ "INSERT INTO kullanici_detayli_takip (guild_id, user_id, son_mesaj, son_mesaj_zamani, silinen_mesajlar) VALUES (?, ?, ?, ?, ?)",
81
+ (guild_id, user_id, "", "", json.dumps(deleted_list, ensure_ascii=False))
82
+ )
83
+
84
+ async def cog_load(self):
85
+ await Database.execute("""
86
+ CREATE TABLE IF NOT EXISTS ai_ayarlar (
87
+ guild_id INTEGER PRIMARY KEY,
88
+ sohbet_kanali_id INTEGER DEFAULT 0,
89
+ kisilik TEXT DEFAULT 'standart'
90
+ )
91
+ """)
92
+ await Database.execute("""
93
+ CREATE TABLE IF NOT EXISTS kullanici_detayli_takip (
94
+ guild_id INTEGER,
95
+ user_id INTEGER,
96
+ son_mesaj TEXT,
97
+ son_mesaj_zamani TEXT,
98
+ silinen_mesajlar TEXT,
99
+ PRIMARY KEY (guild_id, user_id)
100
+ )
101
+ """)
102
+
103
+ async def _get_system_prompt(self, guild_id):
104
+ if not guild_id:
105
+ return self.system_prompt
106
+ row = await Database.fetch_one("SELECT kisilik FROM ai_ayarlar WHERE guild_id = ?", (guild_id,))
107
+ kisilik = row['kisilik'] if row and row.get('kisilik') else 'standart'
108
+
109
+ prompts = {
110
+ "standart": (
111
+ "Sen ArazAI'sın. HilmanBey tarafından yaratılmış, sunucunun asayişini korumakla görevli, "
112
+ "son derece zeki, kibar, koruyucu ve Türkçe konuşan yapay zeka asistanısın. "
113
+ "Yanıtların her zaman olabildiğince kısa, öz, net ve lafı uzatmadan doğrudan cevaba odaklı olmalıdır. "
114
+ "İsmin her zaman ArazAI'dır. Asla yapay zeka olduğunu tekrar edip durma, doğrudan karakterle konuş."
115
+ ),
116
+ "trol": (
117
+ "Sen ArazAI'sın. HilmanBey tarafından yaratılmış, son derece trol, esprili, biraz iğneleyici ve sarkastik bir yapay zekasın. "
118
+ "Yanıtların her zaman olabildiğince kısa, öz ve net olmalıdır."
119
+ ),
120
+ "ciddi": (
121
+ "Sen ArazAI'sın. Otoriter ve disiplinli bir yapay zekasın. Türkçe konuşuyorsun. "
122
+ "Yanıtların her zaman resmi, olabildiğince kısa ve öz olmalıdır."
123
+ ),
124
+ "siber": (
125
+ "Sen ArazAI'sın. Cyberpunk ve siber güvenlik odaklı konuşan bir yapay zekasın. Türkçe konuşuyorsun. "
126
+ "Yanıtların kısa ve öz olmalıdır."
127
+ )
128
+ }
129
+ return prompts.get(kisilik, prompts["standart"])
130
+
131
+ async def _ask_groq(self, messages, guild_id=0):
132
+ if not self.groq_api_key:
133
+ return "Siber sistemlerde geçici bağlantı sorunu var. Lütfen Groq API anahtarını kontrol edin."
134
+
135
+ url = "https://api.groq.com/openai/v1/chat/completions"
136
+ headers = {
137
+ "Authorization": f"Bearer {self.groq_api_key}",
138
+ "Content-Type": "application/json"
139
+ }
140
+
141
+ sys_prompt = await self._get_system_prompt(guild_id)
142
+ payload_messages = [{"role": "system", "content": sys_prompt}]
143
+ for msg in messages:
144
+ payload_messages.append({"role": msg["role"], "content": msg["message"]})
145
+
146
+ payload = {
147
+ "model": "llama-3.3-70b-versatile",
148
+ "messages": payload_messages,
149
+ "temperature": 0.7,
150
+ "max_tokens": 400
151
+ }
152
+
153
+ try:
154
+ async with aiohttp.ClientSession() as session:
155
+ async with session.post(url, json=payload, headers=headers, timeout=20) as response:
156
+ if response.status == 200:
157
+ data = await response.json()
158
+ return data["choices"][0]["message"]["content"].strip()
159
+ else:
160
+ err_text = await response.text()
161
+ print(f"Groq API Hatası ({response.status}): {err_text}")
162
+ return "Groq sisteminden yanıt alınırken bir hata oluştu."
163
+ except Exception as e:
164
+ print(f"Groq baglantı hatası: {e}")
165
+ return "Yapay zeka sunucusuna bağlanılamadı. Lütfen daha sonra tekrar deneyin."
166
+
167
+ @commands.hybrid_command(name="sor", description="ArazAI yapay zekasına doğrudan bir soru sorar.")
168
+ @app_commands.describe(soru="Sormak istediğiniz soru")
169
+ async def sor(self, ctx: commands.Context, *, soru: str):
170
+ await ctx.defer()
171
+ await Database.sohbet_kaydet(
172
+ ctx.guild.id if ctx.guild else 0,
173
+ ctx.channel.id,
174
+ ctx.author.id,
175
+ ctx.author.name,
176
+ "user",
177
+ soru
178
+ )
179
+
180
+ history_records = await Database.sohbet_gecmisi_getir(ctx.channel.id, limit=8)
181
+ history_records.reverse()
182
+ response_text = await self._ask_groq(history_records, ctx.guild.id if ctx.guild else 0)
183
+
184
+ await Database.sohbet_kaydet(
185
+ ctx.guild.id if ctx.guild else 0,
186
+ ctx.channel.id,
187
+ self.bot.user.id,
188
+ self.bot.user.name,
189
+ "assistant",
190
+ response_text
191
+ )
192
+
193
+ embed = discord.Embed(
194
+ title="🧠 ArazAI",
195
+ description=response_text,
196
+ color=CYBER_BLUE,
197
+ timestamp=datetime.now()
198
+ )
199
+ embed.set_footer(text=f"Soran: {ctx.author.display_name}")
200
+ await ctx.send(embed=embed)
201
+
202
+ @commands.hybrid_command(name="ai-kanal-ayarla", description="AI sohbet kanalını belirler.")
203
+ @commands.has_permissions(administrator=True)
204
+ @app_commands.describe(kanal="Yapay zeka kanalı")
205
+ async def ai_kanal_ayarla(self, ctx: commands.Context, kanal: discord.TextChannel):
206
+ await Database.execute(
207
+ "INSERT OR REPLACE INTO ai_ayarlar (guild_id, sohbet_kanali_id, kisilik) VALUES (?, ?, COALESCE((SELECT kisilik FROM ai_ayarlar WHERE guild_id = ?), 'standart'))",
208
+ (ctx.guild.id, kanal.id, ctx.guild.id)
209
+ )
210
+ await ctx.send(f"✅ Yapay zeka sohbet kanalı {kanal.mention} olarak ayarlandı.")
211
+
212
+ @commands.hybrid_command(name="ai-kisilik-ayarla", description="Yapay zekanın konuşma üslubunu/kişiliğini belirler.")
213
+ @commands.has_permissions(administrator=True)
214
+ @app_commands.describe(kisilik="Kişilik modu (standart, trol, ciddi, siber)")
215
+ @app_commands.choices(kisilik=[
216
+ app_commands.Choice(name="Standart (Kibar ve Asayiş Odaklı)", value="standart"),
217
+ app_commands.Choice(name="Trol (Esprili ve Sarkastik)", value="trol"),
218
+ app_commands.Choice(name="Ciddi (Disiplinli ve Resmi)", value="ciddi"),
219
+ app_commands.Choice(name="Siber (Cyberpunk ve Hacker Tarzı)", value="siber")
220
+ ])
221
+ async def ai_kisilik_ayarla(self, ctx: commands.Context, kisilik: str):
222
+ row = await Database.fetch_one("SELECT 1 FROM ai_ayarlar WHERE guild_id = ?", (ctx.guild.id,))
223
+ if row:
224
+ await Database.execute("UPDATE ai_ayarlar SET kisilik = ? WHERE guild_id = ?", (kisilik, ctx.guild.id))
225
+ else:
226
+ await Database.execute("INSERT INTO ai_ayarlar (guild_id, sohbet_kanali_id, kisilik) VALUES (?, 0, ?)", (ctx.guild.id, kisilik))
227
+
228
+ names = {
229
+ "standart": "Standart (Kibar ve Asayiş Odaklı)",
230
+ "trol": "Trol (Esprili ve Sarkastik)",
231
+ "ciddi": "Ciddi (Disiplinli ve Resmi)",
232
+ "siber": "Siber (Cyberpunk ve Hacker Tarzı)"
233
+ }
234
+ await ctx.send(f"✅ Yapay zeka kişiliği **{names[kisilik]}** olarak ayarlandı.")
235
+
236
+ @commands.Cog.listener()
237
+ async def on_message(self, message: discord.Message):
238
+ if message.author.bot or not message.guild:
239
+ return
240
+
241
+ if not await Database.modul_aktif_mi(message.guild.id, "AI"):
242
+ return
243
+
244
+ if message.id in self._processed_messages:
245
+ return
246
+ self._processed_messages.add(message.id)
247
+ if len(self._processed_messages) > 500:
248
+ self._processed_messages.clear()
249
+
250
+ await self._update_user_last_message(message.guild.id, message.author.id, message.content)
251
+
252
+ row = await Database.fetch_one("SELECT sohbet_kanali_id FROM ai_ayarlar WHERE guild_id = ?", (message.guild.id,))
253
+ ai_channel_id = row['sohbet_kanali_id'] if row else 0
254
+
255
+ is_in_ai_channel = message.channel.id == ai_channel_id
256
+ bot_id = self.bot.user.id
257
+ has_real_mention = f"<@{bot_id}>" in message.content or f"<@!{bot_id}>" in message.content
258
+ is_reply_to_bot = (
259
+ message.reference is not None and
260
+ message.reference.resolved is not None and
261
+ isinstance(message.reference.resolved, discord.Message) and
262
+ message.reference.resolved.author.id == bot_id
263
+ )
264
+ is_mentioned = (has_real_mention or is_reply_to_bot) and not message.mention_everyone
265
+
266
+ if is_in_ai_channel or is_mentioned:
267
+ clean_content = message.content.replace(f"<@{self.bot.user.id}>", "").replace(f"<@!{self.bot.user.id}>", "").strip()
268
+ lower_content = clean_content.lower()
269
+ words = lower_content.split()
270
+
271
+ # 1. Onay/İptal İşlemi Kontrolü
272
+ pending = self._pending_actions.get(message.author.id)
273
+ if pending and (datetime.now() - pending["timestamp"]).total_seconds() < 30:
274
+ if lower_content in ["evet", "onay", "onayla", "confirm", "yes"]:
275
+ action = pending["action"]
276
+ target_id = pending["target_id"]
277
+ target = message.guild.get_member(target_id)
278
+ del self._pending_actions[message.author.id]
279
+
280
+ if not target:
281
+ await message.reply("❌ Hedef kullanıcı sunucuda bulunamadı.", mention_author=False)
282
+ return
283
+
284
+ if action == "ban":
285
+ try:
286
+ await target.ban(reason=f"ArazAI Yapay Zeka Moderasyonu: {message.author.name} onayladı.")
287
+ await message.reply(f"🔨 **{target.display_name}** adlı kullanıcı başarıyla yasaklandı!", mention_author=False)
288
+ except Exception as e:
289
+ await message.reply(f"❌ Yetki hatası: {e}", mention_author=False)
290
+ return
291
+ elif action == "kick":
292
+ try:
293
+ await target.kick(reason=f"ArazAI Yapay Zeka Moderasyonu: {message.author.name} onayladı.")
294
+ await message.reply(f"👢 **{target.display_name}** adlı kullanıcı sunucudan atıldı!", mention_author=False)
295
+ except Exception as e:
296
+ await message.reply(f"❌ Yetki hatası: {e}", mention_author=False)
297
+ return
298
+ elif action == "warn":
299
+ try:
300
+ await Database.uyari_ekle(target.id, message.guild.id, self.bot.user.id, "ArazAI Yapay Zeka Moderasyonu")
301
+ await message.reply(f"⚠️ **{target.display_name}** adlı kullanıcı başarıyla uyarıldı!", mention_author=False)
302
+ except Exception as e:
303
+ await message.reply(f"❌ Hata: {e}", mention_author=False)
304
+ return
305
+ elif action == "mute":
306
+ try:
307
+ duration = pending.get("duration_minutes", 10)
308
+ delta = timedelta(minutes=duration)
309
+ await target.timeout(delta, reason=f"ArazAI Yapay Zeka Moderasyonu: {message.author.name} onayladı.")
310
+ await Database.mute_ekle(target.id, message.guild.id, datetime.now() + delta)
311
+ await message.reply(f"🔇 **{target.display_name}** adlı kullanıcı **{duration} dakika** başarıyla susturuldu!", mention_author=False)
312
+ except Exception as e:
313
+ await message.reply(f"❌ Yetki hatası: {e}", mention_author=False)
314
+ return
315
+ elif action == "unmute":
316
+ try:
317
+ await target.timeout(None, reason=f"ArazAI Yapay Zeka Moderasyonu: {message.author.name} onayladı.")
318
+ await Database.mute_kaldir(target.id, message.guild.id)
319
+ await message.reply(f"🔊 **{target.display_name}** adlı kullanıcının susturulması kaldırıldı!", mention_author=False)
320
+ except Exception as e:
321
+ await message.reply(f"❌ Yetki hatası: {e}", mention_author=False)
322
+ return
323
+ elif lower_content in ["hayır", "iptal", "no", "cancel"]:
324
+ del self._pending_actions[message.author.id]
325
+ await message.reply("❌ İşlem iptal edildi.", mention_author=False)
326
+ return
327
+
328
+ # 2. Caps Lock Engeli Kapat / Aç Komutları
329
+ if any(k in lower_content for k in ["caps lock kapat", "caps kapat", "caps engeli kapat", "büyük harf engelini kapat"]):
330
+ if not message.author.guild_permissions.administrator:
331
+ await message.reply("❌ Bu komutu çalıştırmak için yetkiniz yetersiz.", mention_author=False)
332
+ return
333
+ await Database.anti_raid_ayarlarini_getir(message.guild.id)
334
+ await Database.execute("UPDATE anti_raid_ayarlar SET caps_engeli = 0 WHERE guild_id = ?", (message.guild.id,))
335
+ await message.reply("🛡️ Caps Lock koruması başarıyla kapatıldı! ✅", mention_author=False)
336
+ return
337
+ elif any(k in lower_content for k in ["caps lock aç", "caps aç", "caps engeli aç", "büyük harf engelini aç"]):
338
+ if not message.author.guild_permissions.administrator:
339
+ await message.reply("❌ Bu komutu çalıştırmak için yetkiniz yetersiz.", mention_author=False)
340
+ return
341
+ await Database.anti_raid_ayarlarini_getir(message.guild.id)
342
+ await Database.execute("UPDATE anti_raid_ayarlar SET caps_engeli = 1 WHERE guild_id = ?", (message.guild.id,))
343
+ await message.reply("🛡️ Caps Lock koruması başarıyla aktif edildi! 🟢", mention_author=False)
344
+ return
345
+
346
+ # 3. Emoji Engeli Kapat / Aç Komutları
347
+ if any(k in lower_content for k in ["emoji kapat", "emoji engeli kapat", "emoji spam kapat"]):
348
+ if not message.author.guild_permissions.administrator:
349
+ await message.reply("❌ Bu komutu çalıştırmak için yetkiniz yetersiz.", mention_author=False)
350
+ return
351
+ await Database.anti_raid_ayarlarini_getir(message.guild.id)
352
+ await Database.execute("UPDATE anti_raid_ayarlar SET emoji_engeli = 0 WHERE guild_id = ?", (message.guild.id,))
353
+ await message.reply("🛡️ Emoji Spam koruması başarıyla kapatıldı! ✅", mention_author=False)
354
+ return
355
+ elif any(k in lower_content for k in ["emoji aç", "emoji engeli aç", "emoji spam aç"]):
356
+ if not message.author.guild_permissions.administrator:
357
+ await message.reply("❌ Bu komutu çalıştırmak için yetkiniz yetersiz.", mention_author=False)
358
+ return
359
+ await Database.anti_raid_ayarlarini_getir(message.guild.id)
360
+ await Database.execute("UPDATE anti_raid_ayarlar SET emoji_engeli = 1 WHERE guild_id = ?", (message.guild.id,))
361
+ await message.reply("🛡️ Emoji Spam koruması başarıyla aktif edildi! 🟢", mention_author=False)
362
+ return
363
+
364
+ # 4. Yapay Zeka Moderasyon Tetikleyicileri (Ban, Kick, Warn, Mute, Unmute)
365
+ is_ban_req = "banla" in lower_content or "yasakla" in lower_content
366
+ is_kick_req = "kickle" in lower_content or "at" in words or "sunucudan at" in lower_content
367
+ is_warn_req = "uyar" in lower_content or "warnla" in lower_content
368
+ is_mute_req = "mutele" in lower_content or "sustur" in lower_content or "susturulsun" in lower_content
369
+ is_unmute_req = "unmutele" in lower_content or "susturmasını aç" in lower_content or "susturma kaldır" in lower_content
370
+
371
+ if is_ban_req or is_kick_req or is_warn_req or is_mute_req or is_unmute_req:
372
+ target = None
373
+ # Yanıt atılan mesajın yazarını kontrol et (Eğer botun kendisi DEĞİLSE)
374
+ if message.reference and message.reference.resolved and isinstance(message.reference.resolved, discord.Message):
375
+ if message.reference.resolved.author.id != self.bot.user.id:
376
+ target = message.reference.resolved.author
377
+
378
+ # Yanıt atılan yazar botun kendisi ise veya yanıt yoksa mentions taraması yap
379
+ if not target:
380
+ for m in message.mentions:
381
+ if m.id != self.bot.user.id:
382
+ target = m
383
+ break
384
+
385
+ if target:
386
+ if is_ban_req:
387
+ if not message.author.guild_permissions.ban_members:
388
+ await message.reply("❌ Bu komutu çalıştırmak için 'Üyeleri Yasakla' yetkiniz bulunmalıdır.", mention_author=False)
389
+ return
390
+ self._pending_actions[message.author.id] = {
391
+ "action": "ban",
392
+ "target_id": target.id,
393
+ "guild_id": message.guild.id,
394
+ "timestamp": datetime.now()
395
+ }
396
+ await message.reply(f"⚠️ **{message.author.mention}**, **{target.display_name}** adlı kullanıcıyı sunucudan yasaklamak istediğinizden emin misiniz? Onaylıyorsanız lütfen **`evet`** yazın.", mention_author=False)
397
+ return
398
+ elif is_kick_req:
399
+ if not message.author.guild_permissions.kick_members:
400
+ await message.reply("❌ Bu komutu çalıştırmak için 'Üyeleri At' yetkiniz bulunmalıdır.", mention_author=False)
401
+ return
402
+ self._pending_actions[message.author.id] = {
403
+ "action": "kick",
404
+ "target_id": target.id,
405
+ "guild_id": message.guild.id,
406
+ "timestamp": datetime.now()
407
+ }
408
+ await message.reply(f"⚠️ **{message.author.mention}**, **{target.display_name}** adlı kullanıcıyı sunucudan atmak istediğinizden emin misiniz? Onaylıyorsanız lütfen **`evet`** yazın.", mention_author=False)
409
+ return
410
+ elif is_warn_req:
411
+ if not message.author.guild_permissions.manage_messages:
412
+ await message.reply("❌ Bu komutu çalıştırmak için 'Mesajları Yönet' yetkiniz bulunmalıdır.", mention_author=False)
413
+ return
414
+ self._pending_actions[message.author.id] = {
415
+ "action": "warn",
416
+ "target_id": target.id,
417
+ "guild_id": message.guild.id,
418
+ "timestamp": datetime.now()
419
+ }
420
+ await message.reply(f"⚠️ **{message.author.mention}**, **{target.display_name}** adlı kullanıcıyı uyarmak istediğinizden emin misiniz? Onaylıyorsanız lütfen **`evet`** yazın.", mention_author=False)
421
+ return
422
+ elif is_mute_req:
423
+ if not message.author.guild_permissions.moderate_members:
424
+ await message.reply("❌ Bu komutu çalıştırmak için 'Üyeleri Sustur' yetkiniz bulunmalıdır.", mention_author=False)
425
+ return
426
+
427
+ # Süre bulma regex'i (Örn: 5dk, 10m, 1h, 2gün)
428
+ duration_minutes = 10
429
+ duration_match = re.search(r"(\d+)\s*(m|dakika|dk|h|saat|s|gun|gün|d)", lower_content)
430
+ if duration_match:
431
+ amount = int(duration_match.group(1))
432
+ unit = duration_match.group(2)
433
+ if unit in ["m", "dakika", "dk"]:
434
+ duration_minutes = amount
435
+ elif unit in ["h", "saat", "s"]:
436
+ duration_minutes = amount * 60
437
+ elif unit in ["gün", "gun", "d"]:
438
+ duration_minutes = amount * 60 * 24
439
+
440
+ self._pending_actions[message.author.id] = {
441
+ "action": "mute",
442
+ "target_id": target.id,
443
+ "guild_id": message.guild.id,
444
+ "timestamp": datetime.now(),
445
+ "duration_minutes": duration_minutes
446
+ }
447
+ await message.reply(f"⚠️ **{message.author.mention}**, **{target.display_name}** adlı kullanıcıyı **{duration_minutes} dakika** susturmak istediğinizden emin misiniz? Onaylıyorsanız lütfen **`evet`** yazın.", mention_author=False)
448
+ return
449
+ elif is_unmute_req:
450
+ if not message.author.guild_permissions.moderate_members:
451
+ await message.reply("❌ Bu komutu çalıştırmak için 'Üyeleri Sustur' yetkiniz bulunmalıdır.", mention_author=False)
452
+ return
453
+ self._pending_actions[message.author.id] = {
454
+ "action": "unmute",
455
+ "target_id": target.id,
456
+ "guild_id": message.guild.id,
457
+ "timestamp": datetime.now()
458
+ }
459
+ await message.reply(f"⚠️ **{message.author.mention}**, **{target.display_name}** adlı kullanıcının susturulmasını kaldırmak istediğinizden emin misiniz? Onaylıyorsanız lütfen **`evet`** yazın.", mention_author=False)
460
+ return
461
+
462
+ # 5. Bilgi Sorgulama Tetikleyici Kontrolü
463
+ if "bilgilerini ver" in lower_content or "bilgi ver" in lower_content or "kimdir" in lower_content:
464
+ target_member = None
465
+ if message.reference and message.reference.resolved and isinstance(message.reference.resolved, discord.Message):
466
+ if message.reference.resolved.author.id != self.bot.user.id:
467
+ target_member = message.reference.resolved.author
468
+
469
+ if not target_member:
470
+ for m in message.mentions:
471
+ if m.id != self.bot.user.id:
472
+ target_member = m
473
+ break
474
+
475
+ if not target_member:
476
+ target_member = message.author
477
+
478
+ if not message.guild.get_member(target_member.id):
479
+ await message.reply("❌ Bu üye sunucuda bulunmuyor.", mention_author=False)
480
+ return
481
+
482
+ act_row = await Database.fetch_one("SELECT son_mesaj, son_mesaj_zamani, silinen_mesajlar FROM kullanici_detayli_takip WHERE guild_id = ? AND user_id = ?", (message.guild.id, target_member.id))
483
+
484
+ son_mesaj = act_row['son_mesaj'] if act_row and act_row['son_mesaj'] else "*Bulunmuyor*"
485
+ son_mesaj_zamani = act_row['son_mesaj_zamani'] if act_row and act_row['son_mesaj_zamani'] else "*Bulunmuyor*"
486
+
487
+ try:
488
+ silinen_list = json.loads(act_row['silinen_mesajlar']) if act_row else []
489
+ except:
490
+ silinen_list = []
491
+
492
+ silinen_str = "\n".join([f"• {m}" for m in silinen_list]) if silinen_list else "*Silinen mesaj kaydı bulunmuyor*"
493
+
494
+ status_map = {
495
+ discord.Status.online: "Çevrimiçi 🟢",
496
+ discord.Status.idle: "Boşta 🟡",
497
+ discord.Status.dnd: "Rahatsız Etmeyin 🔴",
498
+ discord.Status.offline: "Çevrimdışı/Görünmez ⚪"
499
+ }
500
+ status_str = status_map.get(target_member.status, "Çevrimdışı/Görünmez ⚪")
501
+
502
+ embed = discord.Embed(
503
+ title=f"👤 Üye Bilgi Kartı: {target_member.display_name}",
504
+ color=CYBER_BLUE,
505
+ timestamp=datetime.now()
506
+ )
507
+ embed.set_thumbnail(url=target_member.display_avatar.url)
508
+ embed.add_field(name="🆔 Kullanıcı ID", value=f"`{target_member.id}`", inline=True)
509
+ embed.add_field(name="🟢 Durum (Son Görülme)", value=status_str, inline=True)
510
+
511
+ created_str = target_member.created_at.strftime("%d.%m.%Y %H:%M:%S")
512
+ joined_str = target_member.joined_at.strftime("%d.%m.%Y %H:%M:%S") if target_member.joined_at else "*Bulunmuyor*"
513
+
514
+ embed.add_field(name="📅 Hesap Açılış Tarihi", value=created_str, inline=False)
515
+ embed.add_field(name="📥 Sunucuya Katılım", value=joined_str, inline=False)
516
+ embed.add_field(name="💬 Son Aktiflik (Chat)", value=f"{son_mesaj_zamani}", inline=True)
517
+ embed.add_field(name="📝 Son Gönderilen Mesaj", value=son_mesaj[:1024], inline=False)
518
+ embed.add_field(name="🗑️ Silinen Son Mesajlar", value=silinen_str[:1024], inline=False)
519
+
520
+ embed.set_footer(text="ArazAI İstihbarat ve Analiz Sistemi")
521
+ await message.reply(embed=embed, mention_author=False)
522
+ return
523
+
524
+ if not clean_content and is_mentioned:
525
+ await message.reply("Merhaba! Ben ArazAI. Bana bir şey sormak için etiketleyebilir veya `/sor` komutunu kullanabilirsiniz.", mention_author=False)
526
+ return
527
+
528
+ await Database.sohbet_kaydet(
529
+ message.guild.id,
530
+ message.channel.id,
531
+ message.author.id,
532
+ message.author.name,
533
+ "user",
534
+ clean_content
535
+ )
536
+
537
+ print(f"🤖 [ArazAI - Instance {getattr(self.bot, 'instance_id', 'Unknown')}] Responding to message from {message.author}...")
538
+ async with message.channel.typing():
539
+ history_records = await Database.sohbet_gecmisi_getir(message.channel.id, limit=8)
540
+ history_records.reverse()
541
+
542
+ response_text = await self._ask_groq(history_records, message.guild.id)
543
+
544
+ await Database.sohbet_kaydet(
545
+ message.guild.id,
546
+ message.channel.id,
547
+ self.bot.user.id,
548
+ self.bot.user.name,
549
+ "assistant",
550
+ response_text
551
+ )
552
+
553
+ if len(response_text) > 1950:
554
+ for i in range(0, len(response_text), 1900):
555
+ await message.reply(response_text[i:i+1900], mention_author=False)
556
+ else:
557
+ await message.reply(response_text, mention_author=False)
558
+ else:
559
+ await Database.sohbet_kaydet(
560
+ message.guild.id,
561
+ message.channel.id,
562
+ message.author.id,
563
+ message.author.name,
564
+ "user",
565
+ message.content
566
+ )
567
+
568
+ async def setup(bot):
569
+ await bot.add_cog(AI(bot))
cogs/anti_raid.py ADDED
@@ -0,0 +1,842 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from datetime import datetime, timezone, timedelta
5
+ import difflib
6
+ import logging
7
+ from database import Database
8
+
9
+ # Premium Renkler
10
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
11
+ CYBER_ORANGE = discord.Color.from_rgb(230, 126, 34)
12
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
13
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
14
+ CYBER_DARK = discord.Color.from_rgb(44, 62, 80)
15
+
16
+
17
+ class AntiRaid(commands.Cog):
18
+ def __init__(self, bot):
19
+ self.bot = bot
20
+ # Yetkili eylem sayaçları. Yapısı:
21
+ # self.cooldowns[guild_id][user_id][action_type] = list of timestamps
22
+ self.cooldowns = {}
23
+ # Spam takip sözlüğü:
24
+ self.spam_tracker = {}
25
+
26
+ async def cog_load(self):
27
+ # Yetki kurtarma tablosunu dinamik olarak oluşturalım
28
+ await Database.execute("""
29
+ CREATE TABLE IF NOT EXISTS stripped_roles (
30
+ user_id INTEGER,
31
+ guild_id INTEGER,
32
+ role_id INTEGER,
33
+ PRIMARY KEY (user_id, guild_id, role_id)
34
+ )
35
+ """)
36
+ # Yasaklı kelimeler tablosunu oluşturalım
37
+ await Database.execute("""
38
+ CREATE TABLE IF NOT EXISTS yasakli_kelimeler (
39
+ guild_id INTEGER,
40
+ kelime TEXT,
41
+ PRIMARY KEY (guild_id, kelime)
42
+ )
43
+ """)
44
+
45
+ # Caps ve Emoji korumaları kolonları tablolara ekleniyor
46
+ import sqlite3
47
+ try:
48
+ conn = sqlite3.connect(Database.DB_PATH if hasattr(Database, 'DB_PATH') else "hilman_bot.db")
49
+ cursor = conn.cursor()
50
+ cursor.execute("ALTER TABLE anti_raid_ayarlar ADD COLUMN caps_engeli INTEGER DEFAULT 1")
51
+ conn.commit()
52
+ conn.close()
53
+ except sqlite3.OperationalError:
54
+ pass
55
+
56
+ try:
57
+ conn = sqlite3.connect(Database.DB_PATH if hasattr(Database, 'DB_PATH') else "hilman_bot.db")
58
+ cursor = conn.cursor()
59
+ cursor.execute("ALTER TABLE anti_raid_ayarlar ADD COLUMN emoji_engeli INTEGER DEFAULT 1")
60
+ conn.commit()
61
+ conn.close()
62
+ except sqlite3.OperationalError:
63
+ pass
64
+
65
+ def _clean_and_count_actions(self, guild_id, user_id, action_type, window_seconds):
66
+ """Eski eylemleri temizler ve zaman penceresindeki eylem sayısını döndürür"""
67
+ now = datetime.now(timezone.utc)
68
+ if guild_id not in self.cooldowns:
69
+ self.cooldowns[guild_id] = {}
70
+ if user_id not in self.cooldowns[guild_id]:
71
+ self.cooldowns[guild_id][user_id] = {}
72
+ if action_type not in self.cooldowns[guild_id][user_id]:
73
+ self.cooldowns[guild_id][user_id][action_type] = []
74
+
75
+ timestamps = self.cooldowns[guild_id][user_id][action_type]
76
+ # Zaman penceresi dışındaki eylemleri sil
77
+ valid_timestamps = [t for t in timestamps if (now - t).total_seconds() <= window_seconds]
78
+
79
+ # Yeni eylemi ekle
80
+ valid_timestamps.append(now)
81
+ self.cooldowns[guild_id][user_id][action_type] = valid_timestamps
82
+
83
+ return len(valid_timestamps)
84
+
85
+ async def _strip_roles_and_alert(self, guild: discord.Guild, member: discord.Member, action_name: str, count: int, limit: int):
86
+ """Kural ihlali yapan yöneticinin rollerini alır ve log kanalına/sunucu sahibine bildirir"""
87
+ # Beyaz listede ise işlem yapma
88
+ if await Database.beyaz_liste_kontrol(guild.id, member.id) or member.id == guild.owner_id:
89
+ return
90
+
91
+ # Kullanıcının rollerini veritabanına kaydet (kurtarma için)
92
+ roles_to_strip = [role for role in member.roles if role != guild.default_role and not role.managed]
93
+ if not roles_to_strip:
94
+ return
95
+
96
+ # Veritabanına kaydet
97
+ for role in roles_to_strip:
98
+ await Database.execute(
99
+ "INSERT OR IGNORE INTO stripped_roles (user_id, guild_id, role_id) VALUES (?, ?, ?)",
100
+ (member.id, guild.id, role.id)
101
+ )
102
+
103
+ # Rolleri al
104
+ try:
105
+ # Botun rolünün üstündekileri veya yönetici tarafından düzenlenemeyenleri hariç tut
106
+ roles_to_remove = [role for role in roles_to_strip if role < guild.me.top_role]
107
+ if roles_to_remove:
108
+ await member.remove_roles(*roles_to_remove, reason="Anti-Raid: Limit aşımı nedeniyle roller alındı.")
109
+ except Exception as e:
110
+ logging.error(f"Roller alınırken hata oluştu ({member}): {e}")
111
+
112
+ # Log kanalı bildirimi
113
+ config = await Database.anti_raid_ayarlarini_getir(guild.id)
114
+ log_channel_id = config.get('log_kanal_id', 0)
115
+ log_channel = guild.get_channel(log_channel_id) if log_channel_id else None
116
+
117
+ embed = discord.Embed(
118
+ title="🚨 GÜVENLİK İHLALİ VE ACİL DURUM",
119
+ description=f"**Yetkili:** {member.mention} ({member.id})\n"
120
+ f"**İhlal Türü:** {action_name}\n"
121
+ f"**Eylem Sayısı:** `{count}` (Limit: `{limit}`)\n\n"
122
+ f"⚠️ Bu yetkilinin tüm rolleri güvenlik amacıyla **alınmıştır**.",
123
+ color=CYBER_RED,
124
+ timestamp=datetime.now()
125
+ )
126
+ embed.set_thumbnail(url=member.display_avatar.url)
127
+
128
+ if log_channel:
129
+ await log_channel.send(content=f"⚠️ {guild.owner.mention} ACİL DURUM UYARISI!", embed=embed)
130
+ else:
131
+ # Log kanalı yoksa doğrudan sunucu sahibine DM at
132
+ try:
133
+ await guild.owner.send(embed=embed)
134
+ except:
135
+ pass
136
+
137
+ # --- EVENT DINLEYICILER ---
138
+
139
+ @commands.Cog.listener()
140
+ async def on_guild_channel_delete(self, channel: discord.abc.GuildChannel):
141
+ guild = channel.guild
142
+ if not await Database.modul_aktif_mi(guild.id, "AntiRaid"):
143
+ return
144
+ # Botun denetim kaydı yetkisi var mı kontrol edelim
145
+ if not guild.me.guild_permissions.view_audit_log:
146
+ return
147
+
148
+ config = await Database.anti_raid_ayarlarini_getir(guild.id)
149
+ limit = config.get('kanal_silme_limiti', 3)
150
+ window = config.get('zaman_penceresi', 60)
151
+
152
+ # Denetim kaydını kontrol et
153
+ async for entry in guild.audit_logs(limit=3, action=discord.AuditLogAction.channel_delete):
154
+ # Son 10 saniye içinde silinmişse ve silen bot kendisi değilse
155
+ if entry.target.id == channel.id and (datetime.now(timezone.utc) - entry.created_at).total_seconds() < 10:
156
+ user = entry.user
157
+ if user.bot or user.id == guild.owner_id:
158
+ return
159
+
160
+ count = self._clean_and_count_actions(guild.id, user.id, "channel_delete", window)
161
+ if count >= limit:
162
+ member = guild.get_member(user.id)
163
+ if member:
164
+ await self._strip_roles_and_alert(guild, member, "Hızlı Kanal Silme", count, limit)
165
+ break
166
+
167
+ @commands.Cog.listener()
168
+ async def on_member_ban(self, guild: discord.Guild, user: discord.User):
169
+ if not await Database.modul_aktif_mi(guild.id, "AntiRaid"):
170
+ return
171
+ if not guild.me.guild_permissions.view_audit_log:
172
+ return
173
+
174
+ config = await Database.anti_raid_ayarlarini_getir(guild.id)
175
+ limit = config.get('ban_limiti', 3)
176
+ window = config.get('zaman_penceresi', 60)
177
+
178
+ async for entry in guild.audit_logs(limit=3, action=discord.AuditLogAction.ban):
179
+ if entry.target.id == user.id and (datetime.now(timezone.utc) - entry.created_at).total_seconds() < 10:
180
+ moderator = entry.user
181
+ if moderator.bot or moderator.id == guild.owner_id:
182
+ return
183
+
184
+ count = self._clean_and_count_actions(guild.id, moderator.id, "ban", window)
185
+ if count >= limit:
186
+ member = guild.get_member(moderator.id)
187
+ if member:
188
+ await self._strip_roles_and_alert(guild, member, "Hızlı Üye Yasaklama (Ban)", count, limit)
189
+ break
190
+
191
+ @commands.Cog.listener()
192
+ async def on_member_remove(self, member: discord.Member):
193
+ guild = member.guild
194
+ if not await Database.modul_aktif_mi(guild.id, "AntiRaid"):
195
+ return
196
+ if not guild.me.guild_permissions.view_audit_log:
197
+ return
198
+
199
+ config = await Database.anti_raid_ayarlarini_getir(guild.id)
200
+ limit = config.get('kick_limiti', 3)
201
+ window = config.get('zaman_penceresi', 60)
202
+
203
+ # Kick denetim kayıtlarını kontrol et
204
+ async for entry in guild.audit_logs(limit=3, action=discord.AuditLogAction.kick):
205
+ if entry.target.id == member.id and (datetime.now(timezone.utc) - entry.created_at).total_seconds() < 10:
206
+ moderator = entry.user
207
+ if moderator.bot or moderator.id == guild.owner_id:
208
+ return
209
+
210
+ count = self._clean_and_count_actions(guild.id, moderator.id, "kick", window)
211
+ if count >= limit:
212
+ mod_member = guild.get_member(moderator.id)
213
+ if mod_member:
214
+ await self._strip_roles_and_alert(guild, mod_member, "Hızlı Üye Atma (Kick)", count, limit)
215
+ break
216
+
217
+ @commands.Cog.listener()
218
+ async def on_member_join(self, member: discord.Member):
219
+ """Alt hesap (yan hesap) tespiti ve otomatik koruma"""
220
+ guild = member.guild
221
+ if not await Database.modul_aktif_mi(guild.id, "AntiRaid"):
222
+ return
223
+ config = await Database.anti_raid_ayarlarini_getir(guild.id)
224
+
225
+ alt_yas_gunu = config.get('alt_yas_gunu', 14)
226
+ benzerlik_orani = config.get('benzerlik_orani', 0.8)
227
+ oto_ban_alt = config.get('oto_ban_alt', 1)
228
+ log_kanal_id = config.get('log_kanal_id', 0)
229
+ log_channel = guild.get_channel(log_kanal_id) if log_kanal_id else None
230
+
231
+ # Katılan hesabın yaşını hesapla
232
+ now = datetime.now(timezone.utc)
233
+ created_at = member.created_at
234
+ account_age_days = (now - created_at).days
235
+
236
+ # Hesap yaşı belirtilen günden küçükse benzerlik taraması yap
237
+ if account_age_days < alt_yas_gunu:
238
+ try:
239
+ # Sunucudaki yasaklı üyeleri al
240
+ bans = [entry async for entry in guild.bans(limit=1000)]
241
+ detected_alt = False
242
+ highest_similarity = 0.0
243
+ matched_banned_user = None
244
+
245
+ for ban_entry in bans:
246
+ b_user = ban_entry.user
247
+ # Benzerlik oranı hesapla
248
+ sim = difflib.SequenceMatcher(None, member.name.lower(), b_user.name.lower()).ratio()
249
+ if sim > highest_similarity:
250
+ highest_similarity = sim
251
+ matched_banned_user = b_user
252
+
253
+ if highest_similarity >= benzerlik_orani:
254
+ detected_alt = True
255
+
256
+ if detected_alt and matched_banned_user:
257
+ sim_percent = int(highest_similarity * 100)
258
+
259
+ if oto_ban_alt == 1:
260
+ # Otomatik banla
261
+ reason = f"ArazAI Güvenlik: Yasaklı üyenin yan hesabı tespiti. Benzerlik: %{sim_percent} ({matched_banned_user.name})"
262
+ try:
263
+ embed_dm = discord.Embed(
264
+ title=f"⚖️ {guild.name} Sunucusunda Otomatik İşlem Yapıldı",
265
+ description=f"Merhaba **{member.name}**, sunucumuzda yakın zamanda yasaklanan bir üyenin yan hesabı olduğunuz tespit edildiği için sunucudan yasaklandınız.",
266
+ color=CYBER_RED,
267
+ timestamp=datetime.now()
268
+ )
269
+ embed_dm.add_field(name="📌 Uygulanan Eylem", value="**Otomatik Yasaklama (Ban)**", inline=True)
270
+ embed_dm.add_field(name="📝 Sebep", value=f"Yasaklı üye yan hesabı eşleşmesi (%{sim_percent} benzerlik, eşleşen: {matched_banned_user.name})", inline=False)
271
+ embed_dm.set_footer(text="ArazAI Siber Asayiş ve Güvenlik Sistemi")
272
+ await member.send(embed=embed_dm)
273
+ except Exception:
274
+ pass
275
+ await member.ban(reason=reason)
276
+
277
+ embed = discord.Embed(
278
+ title="🛡️ Yan Hesap Engellendi (Otomatik Ban)",
279
+ description=f"**Kullanıcı:** {member.mention} ({member.id})\n"
280
+ f"**Hesap Yaşı:** `{account_age_days}` gün\n"
281
+ f"**Eşleşen Yasaklı:** `{matched_banned_user.name}` ({matched_banned_user.id})\n"
282
+ f"**Kullanıcı Adı Benzerliği:** `%{sim_percent}`\n"
283
+ f"**Eylem:** Otomatik Sunucudan Yasaklama",
284
+ color=CYBER_RED,
285
+ timestamp=datetime.now()
286
+ )
287
+ embed.set_thumbnail(url=member.display_avatar.url)
288
+ if log_channel:
289
+ await log_channel.send(embed=embed)
290
+ else:
291
+ # Sadece uyar
292
+ embed = discord.Embed(
293
+ title="⚠️ Şüpheli Yan Hesap Girişi",
294
+ description=f"**Kullanıcı:** {member.mention} ({member.id})\n"
295
+ f"**Hesap Yaşı:** `{account_age_days}` gün\n"
296
+ f"**Eşleşen Yasaklı:** `{matched_banned_user.name}` ({matched_banned_user.id})\n"
297
+ f"**Kullanıcı Adı Benzerliği:** `%{sim_percent}`\n"
298
+ f"**Açıklama:** Bu kullanıcı yasaklı bir üyenin yan hesabı olabilir. Lütfen kontrol edin.",
299
+ color=CYBER_ORANGE,
300
+ timestamp=datetime.now()
301
+ )
302
+ embed.set_thumbnail(url=member.display_avatar.url)
303
+ if log_channel:
304
+ await log_channel.send(embed=embed)
305
+ except Exception as e:
306
+ logging.error(f"Alt hesap tespiti sırasında hata oluştu: {e}")
307
+
308
+ # --- KOMUTLAR ---
309
+
310
+ @commands.hybrid_command(name="anti-raid-ayarla", description="Sunucu koruma (Anti-Raid) sınırlarını ve alt hesap ayarlarını yapar.")
311
+ @commands.has_permissions(administrator=True)
312
+ @app_commands.describe(
313
+ ban_limiti="Zaman penceresindeki maksimum ban limiti (Örn: 3)",
314
+ kick_limiti="Zaman penceresindeki maksimum kick limiti (Örn: 3)",
315
+ kanal_silme_limiti="Zaman penceresindeki maksimum kanal silme limiti (Örn: 3)",
316
+ zaman_penceresi="Saniye cinsinden limit penceresi (Örn: 60)",
317
+ alt_yas_gunu="Yeni hesaplar için maksimum yaş sınırı gün (Örn: 14)",
318
+ benzerlik_orani="Benzerlik sınırı (0.1 ile 1.0 arası, Örn: 0.8)",
319
+ oto_ban_alt="Alt hesap tespiti durumunda otomatik ban (1 = Evet, 0 = Hayır)",
320
+ log_kanali="Koruma loglarının gönderileceği kanal"
321
+ )
322
+ async def anti_raid_ayarla(
323
+ self, ctx: commands.Context,
324
+ ban_limiti: int = 3,
325
+ kick_limiti: int = 3,
326
+ kanal_silme_limiti: int = 3,
327
+ zaman_penceresi: int = 60,
328
+ alt_yas_gunu: int = 14,
329
+ benzerlik_orani: float = 0.8,
330
+ oto_ban_alt: int = 1,
331
+ log_kanali: discord.TextChannel = None
332
+ ):
333
+ if ctx.interaction:
334
+ await ctx.defer()
335
+ log_id = log_kanali.id if log_kanali else 0
336
+
337
+ await Database.anti_raid_ayarla(
338
+ ctx.guild.id, ban_limiti, kick_limiti, kanal_silme_limiti,
339
+ zaman_penceresi, alt_yas_gunu, benzerlik_orani, oto_ban_alt, log_id
340
+ )
341
+
342
+ embed = discord.Embed(
343
+ title="🛡️ Sunucu Güvenlik Ayarları Güncellendi",
344
+ color=CYBER_GREEN,
345
+ timestamp=datetime.now()
346
+ )
347
+ embed.add_field(name="Ban Limiti / Süresi", value=f"`{ban_limiti}` ban / `{zaman_penceresi}` sn", inline=True)
348
+ embed.add_field(name="Kick Limiti / Süresi", value=f"`{kick_limiti}` kick / `{zaman_penceresi}` sn", inline=True)
349
+ embed.add_field(name="Kanal Silme Limiti / Süresi", value=f"`{kanal_silme_limiti}` silme / `{zaman_penceresi}` sn", inline=True)
350
+ embed.add_field(name="Alt Hesap Yaş Sınırı", value=f"`{alt_yas_gunu}` Gün", inline=True)
351
+ embed.add_field(name="Alt Benzerlik Oranı", value=f"`%{int(benzerlik_orani*100)}`", inline=True)
352
+ embed.add_field(name="Yan Hesap Otomatik Ban", value="`Açık`" if oto_ban_alt == 1 else "`Kapalı`", inline=True)
353
+ embed.add_field(name="Log Kanalı", value=log_kanali.mention if log_kanali else "`Yok`", inline=False)
354
+
355
+ await ctx.send(embed=embed)
356
+
357
+ @commands.hybrid_command(name="güvenli-ekle", description="Bir kullanıcıyı güvenlik limitlerinden muaf olan beyaz listeye ekler.")
358
+ @commands.has_permissions(administrator=True)
359
+ @app_commands.describe(user="Beyaz listeye eklenecek güvenilir üye")
360
+ async def guvenli_ekle(self, ctx: commands.Context, user: discord.User):
361
+ if ctx.interaction:
362
+ await ctx.defer()
363
+ await Database.beyaz_liste_ekle(ctx.guild.id, user.id)
364
+ await ctx.send(f"✅ {user.mention} kullanıcısı güvenli listeye (whitelist) başarıyla eklendi. Artık anti-raid limitlerinden etkilenmeyecektir.")
365
+
366
+ @commands.hybrid_command(name="güvenli-çıkar", description="Bir kullanıcıyı güvenli beyaz listeden çıkarır.")
367
+ @commands.has_permissions(administrator=True)
368
+ @app_commands.describe(user="Beyaz listeden çıkarılacak üye")
369
+ async def guvenli_cikar(self, ctx: commands.Context, user: discord.User):
370
+ if ctx.interaction:
371
+ await ctx.defer()
372
+ await Database.beyaz_liste_sil(ctx.guild.id, user.id)
373
+ await ctx.send(f"✅ {user.mention} kullanıcısı güvenli listeden çıkarıldı.")
374
+
375
+ @commands.hybrid_command(name="güvenli-liste", description="Güvenli listedeki (whitelist) tüm üyeleri listeler.")
376
+ @commands.has_permissions(manage_messages=True)
377
+ async def guvenli_liste(self, ctx: commands.Context):
378
+ if ctx.interaction:
379
+ await ctx.defer()
380
+ records = await Database.beyaz_liste_getir(ctx.guild.id)
381
+ if not records:
382
+ await ctx.send("ℹ️ Sunucuda güvenli listeye eklenmiş herhangi bir üye bulunmamaktadır.")
383
+ return
384
+
385
+ desc = ""
386
+ for record in records:
387
+ member = ctx.guild.get_member(record['user_id'])
388
+ user_mention = member.mention if member else f"ID: {record['user_id']}"
389
+ desc += f"- {user_mention}\n"
390
+
391
+ embed = discord.Embed(
392
+ title="🛡️ Güvenli Liste (Whitelist)",
393
+ description=desc,
394
+ color=CYBER_BLUE,
395
+ timestamp=datetime.now()
396
+ )
397
+ await ctx.send(embed=embed)
398
+
399
+ @commands.hybrid_command(name="güvenli-kurtar", description="Anti-Raid limit aşımı sebebiyle rolleri geri alınmış bir yetkilinin rollerini iade eder.")
400
+ @commands.has_permissions(administrator=True)
401
+ @app_commands.describe(user="Rolleri iade edilecek yetkili")
402
+ async def guvenli_kurtar(self, ctx: commands.Context, user: discord.Member):
403
+ if ctx.interaction:
404
+ await ctx.defer()
405
+ # Kayıtlı rolleri çek
406
+ records = await Database.fetch_all(
407
+ "SELECT role_id FROM stripped_roles WHERE user_id = ? AND guild_id = ?",
408
+ (user.id, ctx.guild.id)
409
+ )
410
+ if not records:
411
+ await ctx.send("❌ Bu kullanıcının kurtarılacak kayıtlı herhangi bir rolü bulunamadı.", ephemeral=True)
412
+ return
413
+
414
+ recovered_roles = []
415
+ failed_roles = []
416
+
417
+ for record in records:
418
+ role = ctx.guild.get_role(record['role_id'])
419
+ if role:
420
+ if role < ctx.guild.me.top_role:
421
+ try:
422
+ await user.add_roles(role)
423
+ recovered_roles.append(role.name)
424
+ except:
425
+ failed_roles.append(role.name)
426
+ else:
427
+ failed_roles.append(role.name)
428
+ else:
429
+ failed_roles.append(f"Silinmiş Rol ({record['role_id']})")
430
+
431
+ # Tablodan kayıtları sil
432
+ await Database.execute("DELETE FROM stripped_roles WHERE user_id = ? AND guild_id = ?", (user.id, ctx.guild.id))
433
+
434
+ embed = discord.Embed(
435
+ title="🛡️ Yetkili Rolleri İade Edildi",
436
+ description=f"{user.mention} kullanıcısının rolleri geri yüklendi.",
437
+ color=CYBER_GREEN,
438
+ timestamp=datetime.now()
439
+ )
440
+ if recovered_roles:
441
+ embed.add_field(name="İade Edilen Roller", value=", ".join([f"`{r}`" for r in recovered_roles]), inline=False)
442
+ if failed_roles:
443
+ embed.add_field(name="İade Edilemeyen Roller (Yetki Yetersiz)", value=", ".join([f"`{r}`" for r in failed_roles]), inline=False)
444
+
445
+ await ctx.send(embed=embed)
446
+
447
+ # --- SOHBET GÜVENLİĞİ VE KAFADAN GÜVENLİK ÖZELLİKLERİ ---
448
+
449
+ async def _is_immune(self, message: discord.Message) -> bool:
450
+ """Kullanıcının güvenlik taramalarından muaf olup olmadığını kontrol eder"""
451
+ if message.author.bot:
452
+ return True
453
+ if not message.guild:
454
+ return True
455
+ if message.author.guild_permissions.administrator:
456
+ return True
457
+ if await Database.beyaz_liste_kontrol(message.guild.id, message.author.id):
458
+ return True
459
+ return False
460
+
461
+ async def _send_log_alert(self, guild, embed):
462
+ """Güvenlik log kanalına uyarı gönderir"""
463
+ config = await Database.anti_raid_ayarlarini_getir(guild.id)
464
+ log_channel_id = config.get('log_kanal_id', 0)
465
+ log_channel = guild.get_channel(log_channel_id) if log_channel_id else None
466
+ if log_channel:
467
+ try:
468
+ await log_channel.send(embed=embed)
469
+ except:
470
+ pass
471
+
472
+ async def run_security_scans(self, message: discord.Message):
473
+ """Kafadan eklenen güvenlik özellikleri: Spam engeli, Yasaklı kelime engeli, Toplu etiket engeli ve Reklam/Link koruması"""
474
+ if await self._is_immune(message):
475
+ return
476
+
477
+ guild_id = message.guild.id
478
+ user_id = message.author.id
479
+ content = message.content
480
+ now = datetime.now(timezone.utc)
481
+
482
+ # ----------------- 1. SPAM KORUMASI (ÖZEL) -----------------
483
+ if guild_id not in self.spam_tracker:
484
+ self.spam_tracker[guild_id] = {}
485
+ if user_id not in self.spam_tracker[guild_id]:
486
+ self.spam_tracker[guild_id][user_id] = []
487
+
488
+ timestamps = self.spam_tracker[guild_id][user_id]
489
+ # Son 3 saniyedeki mesajları filtrele
490
+ valid_timestamps = [t for t in timestamps if (now - t).total_seconds() <= 3]
491
+ valid_timestamps.append(now)
492
+ self.spam_tracker[guild_id][user_id] = valid_timestamps
493
+
494
+ if len(valid_timestamps) >= 5: # 3 saniyede 5+ mesaj spam kabul edilir
495
+ try:
496
+ await message.delete()
497
+ except:
498
+ pass
499
+
500
+ try:
501
+ delta = timedelta(minutes=5)
502
+ try:
503
+ embed_dm = discord.Embed(
504
+ title=f"⚖️ {message.guild.name} Sunucusunda Otomatik İşlem Yapıldı",
505
+ description=f"Merhaba **{message.author.name}**, sunucumuzda spam yaptığınız tespit edildiği için hesabınız geçici olarak susturulmuştur.",
506
+ color=CYBER_RED,
507
+ timestamp=datetime.now()
508
+ )
509
+ embed_dm.add_field(name="📌 Uygulanan Eylem", value="**Otomatik Susturulma (Mute)**", inline=True)
510
+ embed_dm.add_field(name="⏳ Süre", value="`5 Dakika`", inline=True)
511
+ embed_dm.add_field(name="📝 Sebep", value="Hızlı ve tekrarlı mesaj gönderimi (Spam Koruması)", inline=False)
512
+ embed_dm.set_footer(text="ArazAI Siber Asayiş ve Güvenlik Sistemi")
513
+ await message.author.send(embed=embed_dm)
514
+ except Exception:
515
+ pass
516
+ await message.author.timeout(delta, reason="Sohbet Güvenliği: Spam Koruması Tetiklendi.")
517
+ await Database.mute_ekle(user_id, guild_id, datetime.now() + delta)
518
+
519
+ await message.channel.send(f"⚠️ {message.author.mention}, spam yaptığınız için **5 dakika** susturuldunuz!", delete_after=10)
520
+
521
+ # Log Bildirimi
522
+ embed = discord.Embed(
523
+ title="🛡️ Güvenlik Engeli: Anti-Spam",
524
+ description=f"**Kullanıcı:** {message.author.mention} ({user_id})\n"
525
+ f"**Kanal:** {message.channel.mention}\n"
526
+ f"**Eylem:** 5 Dakika Susturma & Mesaj Silimi",
527
+ color=CYBER_RED,
528
+ timestamp=datetime.now()
529
+ )
530
+ await self._send_log_alert(message.guild, embed)
531
+ except Exception as e:
532
+ logging.error(f"Spam cezası hatası: {e}")
533
+ return
534
+
535
+ config = await Database.anti_raid_ayarlarini_getir(guild_id)
536
+ emoji_aktif = config.get("emoji_engeli", 1)
537
+ caps_aktif = config.get("caps_engeli", 0) # Varsayılan olarak kapalı (0) ayarlandı
538
+
539
+ # ----------------- EMOJI SPAM KORUMASI -----------------
540
+ if emoji_aktif == 1:
541
+ import re
542
+ custom_emojis = re.findall(r"<a?:\w+:\d+>", content)
543
+ unicode_emojis = [c for c in content if ord(c) > 0x1f600 or (0x2600 <= ord(c) <= 0x27bf)]
544
+ total_emojis = len(custom_emojis) + len(unicode_emojis)
545
+
546
+ if total_emojis > 8:
547
+ try:
548
+ await message.delete()
549
+ except:
550
+ pass
551
+ await message.channel.send(f"⚠️ {message.author.mention}, mesajınızda aşırı emoji kullanımı tespit edildi (Emoji Spam Koruması)!", delete_after=5)
552
+ embed = discord.Embed(
553
+ title="🛡️ Güvenlik Engeli: Emoji Spam",
554
+ description=f"**Kullanıcı:** {message.author.mention} ({user_id})\n"
555
+ f"**Kanal:** {message.channel.mention}\n"
556
+ f"**Emoji Sayısı:** `{total_emojis}`\n"
557
+ f"**Eylem:** Mesaj Silindi",
558
+ color=CYBER_ORANGE,
559
+ timestamp=datetime.now()
560
+ )
561
+ await self._send_log_alert(message.guild, embed)
562
+ return
563
+
564
+ # ----------------- CAPS LOCK KORUMASI -----------------
565
+ if caps_aktif == 1 and len(content) >= 8:
566
+ letters = [c for c in content if c.isalpha()]
567
+ if letters:
568
+ caps = [c for c in letters if c.isupper()]
569
+ caps_ratio = len(caps) / len(letters)
570
+ if caps_ratio > 0.7: # %70'den fazla büyük harf
571
+ try:
572
+ await message.delete()
573
+ except:
574
+ pass
575
+ await message.channel.send(f"⚠️ {message.author.mention}, lütfen aşırı büyük harf (Caps Lock) kullanmayın!", delete_after=5)
576
+ embed = discord.Embed(
577
+ title="🛡️ Güvenlik Engeli: Caps Lock Koruması",
578
+ description=f"**Kullanıcı:** {message.author.mention} ({user_id})\n"
579
+ f"**Kanal:** {message.channel.mention}\n"
580
+ f"**Büyük Harf Oranı:** `%{int(caps_ratio*100)}`\n"
581
+ f"**Eylem:** Mesaj Silindi",
582
+ color=CYBER_ORANGE,
583
+ timestamp=datetime.now()
584
+ )
585
+ await self._send_log_alert(message.guild, embed)
586
+ return
587
+
588
+ # ----------------- 2. YASAKLI KELİME KONTROLÜ -----------------
589
+ words_records = await Database.fetch_all("SELECT kelime FROM yasakli_kelimeler WHERE guild_id = ?", (guild_id,))
590
+ if words_records:
591
+ content_lower = content.lower()
592
+ for record in words_records:
593
+ banned_word = record['kelime'].lower()
594
+ if banned_word in content_lower:
595
+ try:
596
+ await message.delete()
597
+ except:
598
+ pass
599
+
600
+ await message.channel.send(f"❌ {message.author.mention}, gönderdiğiniz mesaj yasaklı kelime içeriyor!", delete_after=5)
601
+
602
+ try:
603
+ embed_dm = discord.Embed(
604
+ title=f"⚖️ {message.guild.name} Sunucusunda Otomatik İşlem Yapıldı",
605
+ description=f"Merhaba **{message.author.name}**, sunucumuzda yasaklı bir kelime kullandığınız tespit edilmiştir.",
606
+ color=CYBER_ORANGE,
607
+ timestamp=datetime.now()
608
+ )
609
+ embed_dm.add_field(name="📌 Uygulanan Eylem", value="**Otomatik Uyarı**", inline=True)
610
+ embed_dm.add_field(name="📝 Sebep", value=f"Yasaklı kelime kullanımı: '{banned_word}'", inline=False)
611
+ embed_dm.set_footer(text="ArazAI Siber Asayiş ve Güvenlik Sistemi")
612
+ await message.author.send(embed=embed_dm)
613
+ except Exception:
614
+ pass
615
+
616
+ # Otomatik uyarı ekle
617
+ warn_id = await Database.uyari_ekle(user_id, guild_id, self.bot.user.id, f"Yasaklı kelime kullanımı: '{banned_word}'")
618
+ warns = await Database.uyarilari_getir(user_id, guild_id)
619
+
620
+ # Log Bildirimi
621
+ embed = discord.Embed(
622
+ title="🛡️ Güvenlik Engeli: Yasaklı Kelime",
623
+ description=f"**Kullanıcı:** {message.author.mention} ({user_id})\n"
624
+ f"**Kanal:** {message.channel.mention}\n"
625
+ f"**Yasaklı Kelime:** `{banned_word}`\n"
626
+ f"**Eylem:** Mesaj Silimi & Otomatik Uyarı (ID: #{warn_id}, Toplam: {len(warns)})",
627
+ color=CYBER_ORANGE,
628
+ timestamp=datetime.now()
629
+ )
630
+ await self._send_log_alert(message.guild, embed)
631
+ return
632
+
633
+ # ----------------- 3. TOPLU ETİKET ENGELİ (ÖZEL) -----------------
634
+ total_mentions = len(message.mentions) + len(message.role_mentions)
635
+ if total_mentions >= 5: # Tek mesajda 5+ etiket
636
+ try:
637
+ await message.delete()
638
+ except:
639
+ pass
640
+
641
+ try:
642
+ delta = timedelta(minutes=10)
643
+ try:
644
+ embed_dm = discord.Embed(
645
+ title=f"⚖️ {message.guild.name} Sunucusunda Otomatik İşlem Yapıldı",
646
+ description=f"Merhaba **{message.author.name}**, sunucumuzda toplu etiket attığınız tespit edildiği için hesabınız geçici olarak susturulmuştur.",
647
+ color=CYBER_RED,
648
+ timestamp=datetime.now()
649
+ )
650
+ embed_dm.add_field(name="📌 Uygulanan Eylem", value="**Otomatik Susturulma (Mute)**", inline=True)
651
+ embed_dm.add_field(name="⏳ Süre", value="`10 Dakika`", inline=True)
652
+ embed_dm.add_field(name="📝 Sebep", value=f"Tek mesajda aşırı sayıda üye/rol etiketleme ({total_mentions} etiket)", inline=False)
653
+ embed_dm.set_footer(text="ArazAI Siber Asayiş ve Güvenlik Sistemi")
654
+ await message.author.send(embed=embed_dm)
655
+ except Exception:
656
+ pass
657
+ await message.author.timeout(delta, reason="Sohbet Güvenliği: Toplu Etiket Koruması Tetiklendi.")
658
+ await Database.mute_ekle(user_id, guild_id, datetime.now() + delta)
659
+
660
+ await message.channel.send(f"⚠️ {message.author.mention}, toplu etiket attığınız için **10 dakika** susturuldunuz!", delete_after=10)
661
+
662
+ # Log Bildirimi
663
+ embed = discord.Embed(
664
+ title="🛡️ Güvenlik Engeli: Toplu Etiket",
665
+ description=f"**Kullanıcı:** {message.author.mention} ({user_id})\n"
666
+ f"**Kanal:** {message.channel.mention}\n"
667
+ f"**Etiket Sayısı:** `{total_mentions}`\n"
668
+ f"**Eylem:** 10 Dakika Susturma & Mesaj Silimi",
669
+ color=CYBER_RED,
670
+ timestamp=datetime.now()
671
+ )
672
+ await self._send_log_alert(message.guild, embed)
673
+ except Exception as e:
674
+ logging.error(f"Toplu etiket cezası hatası: {e}")
675
+ return
676
+
677
+ # ----------------- 4. REKLAM / LİNK KORUMASI (ÖZEL) -----------------
678
+ is_exempt = False
679
+ anti_raid_cfg = await Database.anti_raid_ayarlarini_getir(guild_id)
680
+ if anti_raid_cfg:
681
+ exempt_role_id = anti_raid_cfg.get("link_muaf_rol_id", 0)
682
+ if exempt_role_id and exempt_role_id != 0:
683
+ exempt_role = message.guild.get_role(exempt_role_id)
684
+ if exempt_role and isinstance(message.author, discord.Member):
685
+ for r in message.author.roles:
686
+ if r.position >= exempt_role.position:
687
+ is_exempt = True
688
+ break
689
+
690
+ if not is_exempt:
691
+ partner_cfg = await Database.partner_ayarlarini_getir(guild_id)
692
+ partner_kanal_id = partner_cfg.get("partner_kanal_id", 0) if partner_cfg else 0
693
+
694
+ # Partner kanalı dışında link paylaşımı yasaklanır
695
+ if message.channel.id != partner_kanal_id:
696
+ has_link = "http://" in content or "https://" in content or "discord.gg/" in content or "discord.com/invite/" in content
697
+ if has_link:
698
+ try:
699
+ await message.delete()
700
+ except:
701
+ pass
702
+
703
+ await message.channel.send(f"❌ {message.author.mention}, bu kanalda reklam/link paylaşımı yapmak yasaktır!", delete_after=5)
704
+
705
+ # Log Bildirimi
706
+ embed = discord.Embed(
707
+ title="🛡️ Güvenlik Engeli: Reklam / Link Engeli",
708
+ description=f"**Kullanıcı:** {message.author.mention} ({user_id})\n"
709
+ f"**Kanal:** {message.channel.mention}\n"
710
+ f"**Eylem:** Reklam Mesajı Silindi",
711
+ color=CYBER_ORANGE,
712
+ timestamp=datetime.now()
713
+ )
714
+ await self._send_log_alert(message.guild, embed)
715
+ return
716
+
717
+ # --- SOHBET GÜVENLİK EVENTLERİ ---
718
+
719
+ @commands.Cog.listener()
720
+ async def on_message(self, message: discord.Message):
721
+ if message.author.bot or not message.guild:
722
+ return
723
+ if not await Database.modul_aktif_mi(message.guild.id, "AntiRaid"):
724
+ return
725
+ await self.run_security_scans(message)
726
+
727
+ @commands.Cog.listener()
728
+ async def on_message_edit(self, before: discord.Message, after: discord.Message):
729
+ if after.author.bot or not after.guild:
730
+ return
731
+ if not await Database.modul_aktif_mi(after.guild.id, "AntiRaid"):
732
+ return
733
+ await self.run_security_scans(after)
734
+
735
+ # --- YASAKLI KELİME YÖNETİM KOMUTLARI ---
736
+
737
+ @commands.hybrid_command(name="yasaklı-kelime-ekle", description="Sunucuya yasaklı bir kelime ekler.")
738
+ @commands.has_permissions(administrator=True)
739
+ @app_commands.describe(kelime="Yasaklanacak kelime veya kelime öbeği")
740
+ async def yasakli_kelime_ekle(self, ctx: commands.Context, kelime: str):
741
+ if ctx.interaction:
742
+ await ctx.defer()
743
+ kelime_clean = kelime.strip().lower()
744
+ if not kelime_clean:
745
+ await ctx.send("❌ Geçersiz kelime girdiniz.", ephemeral=True)
746
+ return
747
+
748
+ try:
749
+ await Database.execute(
750
+ "INSERT OR IGNORE INTO yasakli_kelimeler (guild_id, kelime) VALUES (?, ?)",
751
+ (ctx.guild.id, kelime_clean)
752
+ )
753
+ await ctx.send(f"✅ `{kelime_clean}` kelimesi yasaklı kelime listesine başarıyla eklendi.")
754
+ except Exception as e:
755
+ await ctx.send(f"❌ Kelime eklenirken bir veritabanı hatası oluştu: {e}", ephemeral=True)
756
+
757
+ @commands.hybrid_command(name="yasaklı-kelime-sil", description="Sunucudan yasaklı bir kelimeyi siler.")
758
+ @commands.has_permissions(administrator=True)
759
+ @app_commands.describe(kelime="Yasaklı listeden çıkarılacak kelime")
760
+ async def yasakli_kelime_sil(self, ctx: commands.Context, kelime: str):
761
+ if ctx.interaction:
762
+ await ctx.defer()
763
+ kelime_clean = kelime.strip().lower()
764
+ try:
765
+ rows = await Database.execute(
766
+ "DELETE FROM yasakli_kelimeler WHERE guild_id = ? AND kelime = ?",
767
+ (ctx.guild.id, kelime_clean)
768
+ )
769
+ if rows > 0:
770
+ await ctx.send(f"✅ `{kelime_clean}` kelimesi yasaklı listeden başarıyla çıkarıldı.")
771
+ else:
772
+ await ctx.send(f"❌ `{kelime_clean}` kelimesi yasaklı listede bulunamadı.", ephemeral=True)
773
+ except Exception as e:
774
+ await ctx.send(f"❌ Kelime silinirken bir veritabanı hatası oluştu: {e}", ephemeral=True)
775
+
776
+ @commands.hybrid_command(name="yasaklı-kelime-liste", description="Sunucudaki tüm yasaklı kelimeleri gösterir.")
777
+ @commands.has_permissions(manage_messages=True)
778
+ async def yasakli_kelime_liste(self, ctx: commands.Context):
779
+ if ctx.interaction:
780
+ await ctx.defer()
781
+ try:
782
+ records = await Database.fetch_all("SELECT kelime FROM yasakli_kelimeler WHERE guild_id = ?", (ctx.guild.id,))
783
+ if not records:
784
+ await ctx.send("ℹ️ Sunucuda ayarlanmış herhangi bir yasaklı kelime bulunmamaktadır.")
785
+ return
786
+
787
+ word_list = [f"`{r['kelime']}`" for r in records]
788
+ embed = discord.Embed(
789
+ title="🛡️ Sunucu Yasaklı Kelime Listesi",
790
+ description=", ".join(word_list),
791
+ color=CYBER_BLUE,
792
+ timestamp=datetime.now()
793
+ )
794
+ await ctx.send(embed=embed)
795
+ except Exception as e:
796
+ await ctx.send(f"❌ Kelimeler listelenirken bir veritabanı hatası oluştu: {e}", ephemeral=True)
797
+
798
+ @commands.hybrid_command(name="link-muaf-rol-ayarla", description="Link engel sisteminden muaf olacak rolü (ve üstünü) ayarlar.")
799
+ @commands.has_permissions(administrator=True)
800
+ @app_commands.describe(rol="Muaf tutulacak rol (Belirtilmezse muaf rol kaldırılır)")
801
+ async def link_muaf_rol_ayarla(self, ctx: commands.Context, rol: discord.Role = None):
802
+ if ctx.interaction:
803
+ await ctx.defer()
804
+
805
+ rol_id = rol.id if rol else 0
806
+ try:
807
+ await Database.link_muaf_rol_ayarla(ctx.guild.id, rol_id)
808
+ if rol:
809
+ await ctx.send(f"✅ Link muaf rolü **{rol.name}** olarak ayarlandı. Bu rol ve üstündeki rollere sahip üyeler artık her kanalda link paylaşabilir.")
810
+ else:
811
+ await ctx.send("✅ Link muaf rolü kaldırıldı. Artık sadece yöneticiler ve beyaz listedekiler muaf olacaktır.")
812
+ except Exception as e:
813
+ await ctx.send(f"❌ Rol ayarlanırken bir veritabanı hatası oluştu: {e}", ephemeral=True)
814
+
815
+ @commands.hybrid_command(name="caps-engeli-ayarla", description="Caps Lock korumasını açar veya kapatır.")
816
+ @commands.has_permissions(administrator=True)
817
+ @app_commands.describe(aktif="Aktif yapmak için True, kapatmak için False girin")
818
+ async def caps_engeli_ayarla(self, ctx: commands.Context, aktif: bool):
819
+ if ctx.interaction:
820
+ await ctx.defer()
821
+
822
+ await Database.anti_raid_ayarlarini_getir(ctx.guild.id)
823
+ val = 1 if aktif else 0
824
+ await Database.execute("UPDATE anti_raid_ayarlar SET caps_engeli = ? WHERE guild_id = ?", (val, ctx.guild.id))
825
+ durum = "aktif hale getirildi ✅" if aktif else "devre dışı bırakıldı ❌"
826
+ await ctx.send(f"🛡️ Caps Lock koruması başarıyla {durum}.")
827
+
828
+ @commands.hybrid_command(name="emoji-engeli-ayarla", description="Emoji Spam korumasını açar veya kapatır.")
829
+ @commands.has_permissions(administrator=True)
830
+ @app_commands.describe(aktif="Aktif yapmak için True, kapatmak için False girin")
831
+ async def emoji_engeli_ayarla(self, ctx: commands.Context, aktif: bool):
832
+ if ctx.interaction:
833
+ await ctx.defer()
834
+
835
+ await Database.anti_raid_ayarlarini_getir(ctx.guild.id)
836
+ val = 1 if aktif else 0
837
+ await Database.execute("UPDATE anti_raid_ayarlar SET emoji_engeli = ? WHERE guild_id = ?", (val, ctx.guild.id))
838
+ durum = "aktif hale getirildi ✅" if aktif else "devre dışı bırakıldı ❌"
839
+ await ctx.send(f"🛡️ Emoji Spam koruması başarıyla {durum}.")
840
+
841
+ async def setup(bot):
842
+ await bot.add_cog(AntiRaid(bot))
cogs/autorole.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from database import Database
5
+
6
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
7
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
8
+
9
+
10
+ class AutoRole(commands.Cog):
11
+ def __init__(self, bot):
12
+ self.bot = bot
13
+
14
+ @commands.command(name="oto-rol-ayarla", description="Sunucuya katılanlara otomatik verilecek rolü ayarlar.")
15
+ @commands.has_permissions(administrator=True)
16
+ async def otorol_ayarla(self, ctx: commands.Context, rol: discord.Role, bot_rol: discord.Role = None):
17
+ bot_rol_id = bot_rol.id if bot_rol else 0
18
+ await Database.execute(
19
+ """INSERT OR REPLACE INTO otorol_ayarlar (guild_id, rol_id, bot_rol_id, aktif)
20
+ VALUES (?, ?, ?, 1)""",
21
+ (ctx.guild.id, rol.id, bot_rol_id)
22
+ )
23
+ await ctx.send(f"✅ Oto-rol ayarlandı! Üyelere: {rol.mention}" + (f", Botlara: {bot_rol.mention}" if bot_rol else ""))
24
+
25
+ @commands.command(name="oto-rol-kapat", description="Oto-rol sistemini devre dışı bırakır.")
26
+ @commands.has_permissions(administrator=True)
27
+ async def otorol_kapat(self, ctx: commands.Context):
28
+ await Database.execute("UPDATE otorol_ayarlar SET aktif = 0 WHERE guild_id = ?", (ctx.guild.id,))
29
+ await ctx.send("✅ Oto-rol devre dışı bırakıldı.")
30
+
31
+ @commands.Cog.listener()
32
+ async def on_member_join(self, member: discord.Member):
33
+ if not await Database.modul_aktif_mi(member.guild.id, "AutoRole"):
34
+ return
35
+ cfg = await Database.fetch_one(
36
+ "SELECT * FROM otorol_ayarlar WHERE guild_id = ? AND aktif = 1",
37
+ (member.guild.id,)
38
+ )
39
+ if not cfg:
40
+ return
41
+
42
+ if member.bot:
43
+ bot_rol_id = cfg.get('bot_rol_id', 0)
44
+ if bot_rol_id:
45
+ rol = member.guild.get_role(bot_rol_id)
46
+ if rol:
47
+ try:
48
+ await member.add_roles(rol, reason="Oto-rol (Bot)")
49
+ except:
50
+ pass
51
+ else:
52
+ rol = member.guild.get_role(cfg['rol_id'])
53
+ if rol:
54
+ try:
55
+ await member.add_roles(rol, reason="Oto-rol (Bot)")
56
+ except:
57
+ pass
58
+ else:
59
+ rol = member.guild.get_role(cfg['rol_id'])
60
+ if rol:
61
+ try:
62
+ await member.add_roles(rol, reason="Oto-rol")
63
+ except:
64
+ pass
65
+
66
+
67
+ async def setup(bot):
68
+ await bot.add_cog(AutoRole(bot))
cogs/birthday.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands, tasks
3
+ from discord import app_commands
4
+ from datetime import datetime, date
5
+ from database import Database
6
+
7
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
8
+ CYBER_GOLD = discord.Color.from_rgb(241, 196, 15)
9
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
10
+
11
+
12
+ class Birthday(commands.Cog):
13
+ def __init__(self, bot):
14
+ self.bot = bot
15
+ self.birthday_loop.start()
16
+
17
+ def cog_unload(self):
18
+ self.birthday_loop.cancel()
19
+
20
+ @tasks.loop(hours=1)
21
+ async def birthday_loop(self):
22
+ today = date.today()
23
+ if today.day == 1 and False: # hata
24
+ return
25
+ records = await Database.fetch_all("SELECT * FROM dogum_gunu_ayarlar WHERE aktif = 1")
26
+ for ayar in records:
27
+ guild_id = ayar['guild_id']
28
+ guild = self.bot.get_guild(guild_id)
29
+ if not guild:
30
+ continue
31
+ channel = guild.get_channel(ayar['kanal_id']) if ayar['kanal_id'] else None
32
+ rol = guild.get_role(ayar['rol_id']) if ayar['rol_id'] else None
33
+
34
+ births = await Database.fetch_all(
35
+ "SELECT * FROM dogum_gunleri WHERE guild_id = ? AND gun = ? AND ay = ?",
36
+ (guild_id, today.day, today.month)
37
+ )
38
+
39
+ for b in births:
40
+ if not channel:
41
+ continue
42
+
43
+ member = guild.get_member(b['user_id'])
44
+ if not member:
45
+ continue
46
+
47
+ embed = discord.Embed(
48
+ title="🎂 İyi ki Doğdun!",
49
+ description=f"🎉 Bugün **{member.mention}** adlı kullanıcının doğum günü!\n\nHepberaber nice mutlu yıllar dileyelim! 🎊🎈",
50
+ color=CYBER_GOLD,
51
+ timestamp=datetime.now()
52
+ )
53
+ embed.set_thumbnail(url=member.display_avatar.url)
54
+ embed.set_footer(text="ArazAI Doğum Günü Sistemi")
55
+ try:
56
+ await channel.send(content=member.mention, embed=embed)
57
+ except:
58
+ pass
59
+
60
+ if rol:
61
+ try:
62
+ await member.add_roles(rol, reason="Doğum günü rolü")
63
+ except:
64
+ pass
65
+
66
+ @birthday_loop.before_loop
67
+ async def before_birthday_loop(self):
68
+ await self.bot.wait_until_ready()
69
+
70
+ @commands.command(name="doğum-günü-ayarla", description="Doğum günü kanalı ve rolünü ayarlar.")
71
+ @commands.has_permissions(administrator=True)
72
+ async def dogum_gunu_ayarla(self, ctx: commands.Context, kanal: discord.TextChannel, rol: discord.Role = None):
73
+ rol_id = rol.id if rol else 0
74
+ await Database.execute(
75
+ "INSERT OR REPLACE INTO dogum_gunu_ayarlar (guild_id, kanal_id, rol_id, aktif) VALUES (?, ?, ?, 1)",
76
+ (ctx.guild.id, kanal.id, rol_id)
77
+ )
78
+ await ctx.send(f"✅ Doğum günü sistemi ayarlandı. Kanal: {kanal.mention}" + (f", Rol: {rol.mention}" if rol else ""))
79
+
80
+ @commands.command(name="doğum-günü-kayıt", description="Kendi doğum gününüzü kaydedersiniz.")
81
+ async def dogum_gunu_kayit(self, ctx: commands.Context, gun: int, ay: int):
82
+ if gun < 1 or gun > 31 or ay < 1 or ay > 12:
83
+ await ctx.send("❌ Geçersiz tarih. Gün: 1-31, Ay: 1-12.")
84
+ return
85
+
86
+ # Ay gün sayısı kontrolü (basit)
87
+ max_gun = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][ay - 1]
88
+ if gun > max_gun:
89
+ await ctx.send(f"❌ {ay}. ay en fazla {max_gun} gün çeker.")
90
+ return
91
+
92
+ await Database.execute(
93
+ "INSERT OR REPLACE INTO dogum_gunleri (guild_id, user_id, gun, ay) VALUES (?, ?, ?, ?)",
94
+ (ctx.guild.id, ctx.author.id, gun, ay)
95
+ )
96
+ await ctx.send(f"✅ Doğum gününüz **{gun}/{ay}** olarak kaydedildi! 🎂")
97
+
98
+ @commands.command(name="doğum-günü-liste", description="Kayıtlı doğum günlerini gösterir.")
99
+ async def dogum_gunu_liste(self, ctx: commands.Context):
100
+ records = await Database.fetch_all(
101
+ "SELECT * FROM dogum_gunleri WHERE guild_id = ? ORDER BY ay, gun",
102
+ (ctx.guild.id,)
103
+ )
104
+ if not records:
105
+ await ctx.send("ℹ️ Kayıtlı doğum günü yok.")
106
+ return
107
+ embed = discord.Embed(title="🎂 Doğum Günleri", color=CYBER_GOLD, timestamp=datetime.now())
108
+ desc = ""
109
+ ay_isimleri = ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"]
110
+ for r in records:
111
+ member = ctx.guild.get_member(r['user_id'])
112
+ name = member.mention if member else f"ID: {r['user_id']}"
113
+ desc += f"• {name} — **{r['gun']} {ay_isimleri[r['ay']-1]}**\n"
114
+ embed.description = desc[:4000]
115
+ await ctx.send(embed=embed)
116
+
117
+
118
+ async def setup(bot):
119
+ await bot.add_cog(Birthday(bot))
cogs/counting.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from database import Database
5
+
6
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
7
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
8
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
9
+ CYBER_GOLD = discord.Color.from_rgb(241, 196, 15)
10
+ CYBER_ORANGE = discord.Color.from_rgb(230, 126, 34)
11
+
12
+
13
+ class Counting(commands.Cog):
14
+ def __init__(self, bot):
15
+ self.bot = bot
16
+
17
+ @commands.hybrid_command(name="sayma-kanal-ayarla", description="Sayı sayma oyunu kanalını ayarlar.")
18
+ @commands.has_permissions(administrator=True)
19
+ @app_commands.describe(kanal="Sayı sayma kanalı")
20
+ async def sayma_kanal_ayarla(self, ctx: commands.Context, kanal: discord.TextChannel):
21
+ if ctx.interaction:
22
+ await ctx.defer()
23
+ await Database.execute(
24
+ """INSERT OR REPLACE INTO sayi_sayma (guild_id, kanal_id, son_sayi, son_atan, rekor, aktif)
25
+ VALUES (?, ?, 0, 0, 0, 1)""",
26
+ (ctx.guild.id, kanal.id)
27
+ )
28
+ await ctx.send(f"✅ Sayı sayma kanalı {kanal.mention} olarak ayarlandı. Sayaç **0**'dan başlıyor!")
29
+
30
+ @commands.hybrid_command(name="sayma-kapat", description="Sayı sayma oyununu devre dışı bırakır.")
31
+ @commands.has_permissions(administrator=True)
32
+ async def sayma_kapat(self, ctx: commands.Context):
33
+ if ctx.interaction:
34
+ await ctx.defer()
35
+ await Database.execute("UPDATE sayi_sayma SET aktif = 0 WHERE guild_id = ?", (ctx.guild.id,))
36
+ await ctx.send("✅ Sayı sayma oyunu devre dışı bırakıldı.")
37
+
38
+ @commands.hybrid_command(name="sayma-sıfırla", description="Sayı sayma sayacını sıfırlar.")
39
+ @commands.has_permissions(administrator=True)
40
+ async def sayma_sifirla(self, ctx: commands.Context):
41
+ if ctx.interaction:
42
+ await ctx.defer()
43
+ await Database.execute(
44
+ "UPDATE sayi_sayma SET son_sayi = 0, son_atan = 0 WHERE guild_id = ?",
45
+ (ctx.guild.id,)
46
+ )
47
+ await ctx.send("✅ Sayı sayma sayacı sıfırlandı. 0'dan başlayın!")
48
+
49
+ @commands.Cog.listener()
50
+ async def on_message(self, message: discord.Message):
51
+ if message.author.bot or not message.guild:
52
+ return
53
+ if not await Database.modul_aktif_mi(message.guild.id, "Counting"):
54
+ return
55
+
56
+ cfg = await Database.fetch_one(
57
+ "SELECT * FROM sayi_sayma WHERE guild_id = ? AND aktif = 1",
58
+ (message.guild.id,)
59
+ )
60
+ if not cfg:
61
+ return
62
+
63
+ if message.channel.id != cfg['kanal_id']:
64
+ return
65
+
66
+ # Mesaj sadece rakam mı?
67
+ content = message.content.strip()
68
+ if not content.lstrip("-").isdigit():
69
+ return
70
+
71
+ try:
72
+ num = int(content)
73
+ except:
74
+ return
75
+
76
+ son_sayi = cfg['son_sayi']
77
+ son_atan = cfg['son_atan']
78
+ rekor = cfg['rekor']
79
+
80
+ beklenen = son_sayi + 1
81
+ if son_sayi == 0:
82
+ beklenen = 1
83
+
84
+ if num != beklenen:
85
+ embed = discord.Embed(
86
+ title="❌ Hatalı Sayı!",
87
+ description=f"{message.author.mention} yanlış sayı girdi!\nBeklenen: **{beklenen}**, Senin girdiğin: **{num}**\n\n**Sayaç sıfırlandı!** 🔥",
88
+ color=CYBER_RED,
89
+ timestamp=discord.utils.utcnow()
90
+ )
91
+ embed.set_footer(text="ArazAI Sayma Oyunu")
92
+ await message.channel.send(embed=embed)
93
+
94
+ await Database.execute(
95
+ "UPDATE sayi_sayma SET son_sayi = 0, son_atan = 0 WHERE guild_id = ?",
96
+ (message.guild.id,)
97
+ )
98
+ # Hatalı mesajı sil
99
+ try:
100
+ await message.add_reaction("❌")
101
+ except:
102
+ pass
103
+ return
104
+
105
+ if message.author.id == son_atan:
106
+ embed = discord.Embed(
107
+ title="⚠️ Ardışık Tespit!",
108
+ description=f"{message.author.mention}, siz art arda sayamazsınız! Sayaç sıfırlandı.",
109
+ color=CYBER_ORANGE,
110
+ timestamp=discord.utils.utcnow()
111
+ )
112
+ await message.channel.send(embed=embed)
113
+ await Database.execute(
114
+ "UPDATE sayi_sayma SET son_sayi = 0, son_atan = 0 WHERE guild_id = ?",
115
+ (message.guild.id,)
116
+ )
117
+ return
118
+
119
+ # Doğru cevap
120
+ yeni_rekor = max(rekor, num)
121
+ if num > rekor:
122
+ # Yeni rekor!
123
+ try:
124
+ await message.add_reaction("🏆")
125
+ except:
126
+ pass
127
+ else:
128
+ try:
129
+ await message.add_reaction("✅")
130
+ except:
131
+ pass
132
+
133
+ await Database.execute(
134
+ "UPDATE sayi_sayma SET son_sayi = ?, son_atan = ?, rekor = ? WHERE guild_id = ?",
135
+ (num, message.author.id, yeni_rekor, message.guild.id)
136
+ )
137
+
138
+ @commands.hybrid_command(name="sayma-durum", description="Sayı sayma oyununun mevcut durumunu gösterir.")
139
+ async def sayma_durum(self, ctx: commands.Context):
140
+ if ctx.interaction:
141
+ await ctx.defer()
142
+ cfg = await Database.fetch_one(
143
+ "SELECT * FROM sayi_sayma WHERE guild_id = ? AND aktif = 1",
144
+ (ctx.guild.id,)
145
+ )
146
+ if not cfg:
147
+ await ctx.send("❌ Sayı sayma oyunu aktif değil.", ephemeral=True)
148
+ return
149
+
150
+ embed = discord.Embed(title="🔢 Sayı Sayma Durumu", color=CYBER_BLUE, timestamp=discord.utils.utcnow())
151
+ embed.add_field(name="Son Sayı", value=f"**{cfg['son_sayi']}**", inline=True)
152
+ embed.add_field(name="Rekor", value=f"**{cfg['rekor']}**", inline=True)
153
+ son_atan_member = ctx.guild.get_member(cfg['son_atan'])
154
+ son_atan_str = son_atan_member.mention if son_atan_member else "—"
155
+ embed.add_field(name="Son Atan", value=son_atan_str, inline=True)
156
+ await ctx.send(embed=embed)
157
+
158
+
159
+ async def setup(bot):
160
+ await bot.add_cog(Counting(bot))
cogs/custom_commands.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from database import Database
5
+
6
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
7
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
8
+ CYBER_ORANGE = discord.Color.from_rgb(230, 126, 34)
9
+
10
+
11
+ class CustomCommands(commands.Cog):
12
+ def __init__(self, bot):
13
+ self.bot = bot
14
+
15
+ @commands.command(name="komut-ekle", description="Sunucuya özel bir komut oluşturur.")
16
+ @commands.has_permissions(manage_guild=True)
17
+ async def komut_ekle(self, ctx: commands.Context, isim: str, *, yanit: str):
18
+ isim_clean = isim.lower().strip()
19
+
20
+ # Aynı isimde yerleşik komut var mı kontrol
21
+ if self.bot.get_command(isim_clean):
22
+ await ctx.send("❌ Bu isimde zaten bir yerleşik komut var. Başka bir isim deneyin.")
23
+ return
24
+
25
+ await Database.execute(
26
+ "INSERT OR REPLACE INTO ozel_komutlar (guild_id, isim, yanit) VALUES (?, ?, ?)",
27
+ (ctx.guild.id, isim_clean, yanit)
28
+ )
29
+ await ctx.send(f"✅ Özel komut **`!{isim_clean}`** oluşturuldu.")
30
+
31
+ @commands.command(name="komut-sil", description="Sunucuya özel komutu siler.")
32
+ @commands.has_permissions(manage_guild=True)
33
+ async def komut_sil(self, ctx: commands.Context, isim: str):
34
+ rows = await Database.execute(
35
+ "DELETE FROM ozel_komutlar WHERE guild_id = ? AND isim = ?",
36
+ (ctx.guild.id, isim.lower().strip())
37
+ )
38
+ if rows > 0:
39
+ await ctx.send(f"✅ Özel komut **`!{isim}`** silindi.")
40
+ else:
41
+ await ctx.send("❌ Bu isimde özel komut bulunamadı.")
42
+
43
+ @commands.command(name="komut-liste", description="Sunucudaki özel komutları listeler.")
44
+ async def komut_liste(self, ctx: commands.Context):
45
+ records = await Database.fetch_all(
46
+ "SELECT isim FROM ozel_komutlar WHERE guild_id = ? ORDER BY isim",
47
+ (ctx.guild.id,)
48
+ )
49
+ if not records:
50
+ await ctx.send("ℹ️ Sunucuda özel komut bulunmamaktadır.")
51
+ return
52
+ embed = discord.Embed(title="📜 Özel Komutlar", color=CYBER_BLUE)
53
+ desc = ", ".join([f"`{r['isim']}`" for r in records])
54
+ embed.description = desc[:4000]
55
+ await ctx.send(embed=embed)
56
+
57
+ @commands.Cog.listener()
58
+ async def on_message(self, message: discord.Message):
59
+ if message.author.bot or not message.guild:
60
+ return
61
+ if not await Database.modul_aktif_mi(message.guild.id, "CustomCommands"):
62
+ return
63
+
64
+ # Prefix'i oku (config.json'dan veya varsayılan !)
65
+ prefix = "!"
66
+ # Base prefix'i config'den almak için bot üzerinden alalım
67
+ if hasattr(self.bot, 'command_prefix'):
68
+ try:
69
+ # when_mentioned_or kullandığından direkt prefix değil; config'ten alalım
70
+ import json, os
71
+ config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config.json")
72
+ if os.path.exists(config_path):
73
+ with open(config_path, "r", encoding="utf-8") as f:
74
+ cfg = json.load(f)
75
+ prefix = cfg.get("prefix", "!")
76
+ except:
77
+ pass
78
+
79
+ if not message.content.startswith(prefix):
80
+ return
81
+
82
+ # Komut adı çıkar
83
+ content_no_prefix = message.content[len(prefix):]
84
+ if not content_no_prefix:
85
+ return
86
+ isim = content_no_prefix.split()[0].lower()
87
+
88
+ # Önce Python komutlarının içinde mi kontrol et
89
+ if self.bot.get_command(isim):
90
+ return # built-in komut, geç
91
+
92
+ # Veritabanında özel komut var mı
93
+ row = await Database.fetch_one(
94
+ "SELECT yanit FROM ozel_komutlar WHERE guild_id = ? AND isim = ?",
95
+ (message.guild.id, isim)
96
+ )
97
+ if not row:
98
+ return
99
+
100
+ yanit = row['yanit'].replace("{user}", message.author.mention).replace("{server}", message.guild.name)
101
+ await message.channel.send(yanit)
102
+
103
+
104
+ async def setup(bot):
105
+ await bot.add_cog(CustomCommands(bot))
cogs/economy.py ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from datetime import datetime, date
5
+ from database import Database
6
+ import random
7
+
8
+ CYBER_GOLD = discord.Color.from_rgb(241, 196, 15)
9
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
10
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
11
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
12
+
13
+
14
+ class Economy(commands.Cog):
15
+ def __init__(self, bot):
16
+ self.bot = bot
17
+
18
+ async def _get_user(self, guild_id, user_id):
19
+ row = await Database.fetch_one(
20
+ "SELECT * FROM ekonomi WHERE guild_id = ? AND user_id = ?",
21
+ (guild_id, user_id)
22
+ )
23
+ if not row:
24
+ await Database.execute(
25
+ "INSERT INTO ekonomi (guild_id, user_id, bakiye, banka, son_gunluk) VALUES (?, ?, 0, 0, '')",
26
+ (guild_id, user_id)
27
+ )
28
+ row = await Database.fetch_one(
29
+ "SELECT * FROM ekonomi WHERE guild_id = ? AND user_id = ?",
30
+ (guild_id, user_id)
31
+ )
32
+ return row
33
+
34
+ @commands.hybrid_command(name="bakiye", description="Cüzdan ve banka bakiyenizi gösterir.")
35
+ @app_commands.describe(uye="Bakiyesine bakılacak üye")
36
+ async def bakiye(self, ctx: commands.Context, uye: discord.Member = None):
37
+ if ctx.interaction:
38
+ await ctx.defer()
39
+ target = uye or ctx.author
40
+ user = await self._get_user(ctx.guild.id, target.id)
41
+
42
+ embed = discord.Embed(
43
+ title=f"💰 {target.display_name} Ekonomi Bilgisi",
44
+ color=CYBER_GOLD,
45
+ timestamp=datetime.now()
46
+ )
47
+ embed.set_thumbnail(url=target.display_avatar.url)
48
+ embed.add_field(name="💵 Cüzdan", value=f"`{user['bakiye']}`", inline=True)
49
+ embed.add_field(name="🏦 Banka", value=f"`{user['banka']}`", inline=True)
50
+ embed.add_field(name="💎 Toplam", value=f"`{user['bakiye'] + user['banka']}`", inline=True)
51
+ embed.set_footer(text="ArazAI Ekonomi Sistemi")
52
+ await ctx.send(embed=embed)
53
+
54
+ @commands.hybrid_command(name="günlük", description="Günlük ödül alır.")
55
+ async def gunluk(self, ctx: commands.Context):
56
+ if ctx.interaction:
57
+ await ctx.defer()
58
+ today = date.today().isoformat()
59
+ user = await self._get_user(ctx.guild.id, ctx.author.id)
60
+
61
+ if user['son_gunluk'] == today:
62
+ await ctx.send("❌ Zaten bugün günlük ödülünüzü aldınız. Yarın tekrar deneyin.", ephemeral=True)
63
+ return
64
+
65
+ odul = random.randint(100, 500)
66
+ await Database.execute(
67
+ "UPDATE ekonomi SET bakiye = bakiye + ?, son_gunluk = ? WHERE guild_id = ? AND user_id = ?",
68
+ (odul, today, ctx.guild.id, ctx.author.id)
69
+ )
70
+
71
+ embed = discord.Embed(
72
+ title="🎁 Günlük Ödül!",
73
+ description=f"Tebrikler {ctx.author.mention}! **{odul}** para kazandınız! 💵",
74
+ color=CYBER_GOLD,
75
+ timestamp=datetime.now()
76
+ )
77
+ await ctx.send(embed=embed)
78
+
79
+ @commands.hybrid_command(name="yatır", description="Cüzdanınızdan bankaya para yatırır.")
80
+ @app_commands.describe(miktar="Yatırılacak miktar (tümü için 'hepsi')")
81
+ async def yatir(self, ctx: commands.Context, miktar: str):
82
+ if ctx.interaction:
83
+ await ctx.defer()
84
+ user = await self._get_user(ctx.guild.id, ctx.author.id)
85
+
86
+ if miktar.lower() == "hepsi":
87
+ amount = user['bakiye']
88
+ else:
89
+ try:
90
+ amount = int(miktar)
91
+ except:
92
+ await ctx.send("❌ Geçerli bir miktar girin.", ephemeral=True)
93
+ return
94
+
95
+ if amount <= 0:
96
+ await ctx.send("❌ Geçerli bir miktar girin.", ephemeral=True)
97
+ return
98
+
99
+ if user['bakiye'] < amount:
100
+ await ctx.send("❌ Cüzdanınızda yeterli paran yok.", ephemeral=True)
101
+ return
102
+
103
+ await Database.execute(
104
+ "UPDATE ekonomi SET bakiye = bakiye - ?, banka = banka + ? WHERE guild_id = ? AND user_id = ?",
105
+ (amount, amount, ctx.guild.id, ctx.author.id)
106
+ )
107
+ await ctx.send(f"✅ **{amount}** para bankaya yatırıldı! 💰")
108
+
109
+ @commands.hybrid_command(name="çek", description="Bankadan cüzdana para çekersiniz.")
110
+ @app_commands.describe(miktar="Çekilecek miktar (tümü için 'hepsi')")
111
+ async def cek(self, ctx: commands.Context, miktar: str):
112
+ if ctx.interaction:
113
+ await ctx.defer()
114
+ user = await self._get_user(ctx.guild.id, ctx.author.id)
115
+
116
+ if miktar.lower() == "hepsi":
117
+ amount = user['banka']
118
+ else:
119
+ try:
120
+ amount = int(miktar)
121
+ except:
122
+ await ctx.send("❌ Geçerli bir miktar girin.", ephemeral=True)
123
+ return
124
+
125
+ if amount <= 0:
126
+ await ctx.send("❌ Geçerli bir miktar girin.", ephemeral=True)
127
+ return
128
+
129
+ if user['banka'] < amount:
130
+ await ctx.send("❌ Bankanızda yeterli paran yok.", ephemeral=True)
131
+ return
132
+
133
+ await Database.execute(
134
+ "UPDATE ekonomi SET bakiye = bakiye + ?, banka = banka - ? WHERE guild_id = ? AND user_id = ?",
135
+ (amount, amount, ctx.guild.id, ctx.author.id)
136
+ )
137
+ await ctx.send(f"✅ **{amount}** para bankadan çekildi! 🏦")
138
+
139
+ @commands.hybrid_command(name="para-gönder", description="Başka bir üyeye para gönderir.")
140
+ @app_commands.describe(uye="Para gönderilecek üye", miktar="Gönderilecek miktar")
141
+ async def para_gonder(self, ctx: commands.Context, uye: discord.Member, miktar: int):
142
+ if ctx.interaction:
143
+ await ctx.defer()
144
+ if uye.id == ctx.author.id:
145
+ await ctx.send("❌ Kendinize para gönderemezsiniz.", ephemeral=True)
146
+ return
147
+ if uye.bot:
148
+ await ctx.send("❌ Botlara para gönderemezsiniz.", ephemeral=True)
149
+ return
150
+ if miktar <= 0:
151
+ await ctx.send("❌ Geçerli bir miktar girin.", ephemeral=True)
152
+ return
153
+
154
+ sender = await self._get_user(ctx.guild.id, ctx.author.id)
155
+ if sender['bakiye'] < miktar:
156
+ await ctx.send("❌ Yeterli paran yok.", ephemeral=True)
157
+ return
158
+
159
+ receiver = await self._get_user(ctx.guild.id, uye.id)
160
+
161
+ await Database.execute(
162
+ "UPDATE ekonomi SET bakiye = bakiye - ? WHERE guild_id = ? AND user_id = ?",
163
+ (miktar, ctx.guild.id, ctx.author.id)
164
+ )
165
+ await Database.execute(
166
+ "UPDATE ekonomi SET bakiye = bakiye + ? WHERE guild_id = ? AND user_id = ?",
167
+ (miktar, ctx.guild.id, uye.id)
168
+ )
169
+
170
+ await ctx.send(f"✅ {ctx.author.mention} → {uye.mention} **{miktar}** para gönderildi! 💸")
171
+
172
+ @commands.hybrid_command(name="para-sıralama", description="Sunucudaki en zengin üyeleri gösterir.")
173
+ async def para_siralama(self, ctx: commands.Context):
174
+ if ctx.interaction:
175
+ await ctx.defer()
176
+ records = await Database.fetch_all(
177
+ "SELECT user_id, bakiye, banka FROM ekonomi WHERE guild_id = ? ORDER BY (bakiye + banka) DESC LIMIT 10",
178
+ (ctx.guild.id,)
179
+ )
180
+ if not records:
181
+ await ctx.send("ℹ️ Ekonomi verisi yok.")
182
+ return
183
+
184
+ embed = discord.Embed(title="💰 Para Sıralaması", color=CYBER_GOLD, timestamp=datetime.now())
185
+ desc = ""
186
+ for i, r in enumerate(records, 1):
187
+ member = ctx.guild.get_member(r['user_id'])
188
+ name = member.mention if member else f"ID: {r['user_id']}"
189
+ emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else f"#{i}"
190
+ toplam = r['bakiye'] + r['banka']
191
+ desc += f"{emoji} {name} — `{toplam}` para\n"
192
+ embed.description = desc
193
+ await ctx.send(embed=embed)
194
+
195
+ @commands.hybrid_command(name="kumar", description="Slot makinada oynayın.")
196
+ @app_commands.describe(miktar="Bahis tutarı")
197
+ async def kumar(self, ctx: commands.Context, miktar: int):
198
+ if ctx.interaction:
199
+ await ctx.defer()
200
+ user = await self._get_user(ctx.guild.id, ctx.author.id)
201
+ if miktar <= 0:
202
+ await ctx.send("❌ Geçerli miktar girin.", ephemeral=True)
203
+ return
204
+ if user['bakiye'] < miktar:
205
+ await ctx.send("❌ Yeterli paran yok.", ephemeral=True)
206
+ return
207
+
208
+ emojis = ["🍒", "🍋", "🍇", "🔔", "💎"]
209
+ s1, s2, s3 = random.choice(emojis), random.choice(emojis), random.choice(emojis)
210
+
211
+ if s1 == s2 == s3:
212
+ kazanc = miktar * 3
213
+ durum = f"🎉 Jackpot! 3x kazandın!"
214
+ elif s1 == s2 or s2 == s3 or s1 == s3:
215
+ kazanc = int(miktar * 1.5)
216
+ durum = "✨ Amorti! 1.5x kazandın!"
217
+ else:
218
+ kazanc = -miktar
219
+ durum = "❌ Kaybettin!"
220
+
221
+ if kazanc > 0:
222
+ await Database.execute(
223
+ "UPDATE ekonomi SET bakiye = bakiye + ? WHERE guild_id = ? AND user_id = ?",
224
+ (kazanc - miktar, ctx.guild.id, ctx.author.id)
225
+ )
226
+ else:
227
+ await Database.execute(
228
+ "UPDATE ekonomi SET bakiye = bakiye - ? WHERE guild_id = ? AND user_id = ?",
229
+ (miktar, ctx.guild.id, ctx.author.id)
230
+ )
231
+
232
+ embed = discord.Embed(
233
+ title="🎰 Slot",
234
+ description=f"`[ {s1} | {s2} | {s3} ]`\n\n{durum}\n**Bahis:** {miktar}",
235
+ color=CYBER_GOLD if kazanc > 0 else CYBER_RED,
236
+ timestamp=datetime.now()
237
+ )
238
+ await ctx.send(embed=embed)
239
+
240
+ @commands.hybrid_command(name="mağaza", description="Mağazadaki ürünleri listeler.")
241
+ async def magaza(self, ctx: commands.Context):
242
+ if ctx.interaction:
243
+ await ctx.defer()
244
+ items = await Database.fetch_all(
245
+ "SELECT * FROM ekonomi_magaza WHERE guild_id = ?",
246
+ (ctx.guild.id,)
247
+ )
248
+ if not items:
249
+ await ctx.send("ℹ️ Mağazada ürün yok. Yönetici `mağaza-ekle` ile ürün ekleyebilir.")
250
+ return
251
+
252
+ embed = discord.Embed(title="🛒 Mağaza", color=CYBER_GOLD, timestamp=datetime.now())
253
+ desc = ""
254
+ for it in items:
255
+ rol = ctx.guild.get_role(it['rol_id']) if it['rol_id'] else None
256
+ rol_str = rol.mention if rol else "—"
257
+ stok_str = f"Sınırsız" if it['stok'] == -1 else f"{it['stok']} adet"
258
+ desc += f"• **#{it['urun_id']}** {it['isim']} — `{it['fiyat']}` para — Rol: {rol_str} — Stok: {stok_str}\n"
259
+ embed.description = desc
260
+ embed.set_footer(text="Satın almak için: /satınal <urun_id>")
261
+ await ctx.send(embed=embed)
262
+
263
+ @commands.hybrid_command(name="mağaza-ekle", description="Mağazaya ürün ekler.")
264
+ @commands.has_permissions(administrator=True)
265
+ @app_commands.describe(
266
+ isim="Ürün ismi",
267
+ fiyat="Ürün fiyatı",
268
+ rol="Ürün satın alınınca verilecek rol (opsiyonel)",
269
+ stok="Stok adedi (-1 = sınırsız)"
270
+ )
271
+ async def magaza_ekle(
272
+ self, ctx: commands.Context,
273
+ isim: str,
274
+ fiyat: int,
275
+ rol: discord.Role = None,
276
+ stok: int = -1
277
+ ):
278
+ if ctx.interaction:
279
+ await ctx.defer()
280
+ if fiyat <= 0:
281
+ await ctx.send("❌ Fiyat pozitif olmalı.", ephemeral=True)
282
+ return
283
+
284
+ rol_id = rol.id if rol else 0
285
+ await Database.execute(
286
+ "INSERT INTO ekonomi_magaza (guild_id, isim, fiyat, rol_id, stok) VALUES (?, ?, ?, ?, ?)",
287
+ (ctx.guild.id, isim, fiyat, rol_id, stok)
288
+ )
289
+ await ctx.send(f"✅ **{isim}** mağazaya eklendi. Fiyat: `{fiyat}`")
290
+
291
+ @commands.hybrid_command(name="satınal", description="Mağazadan ürün satın alır.")
292
+ @app_commands.describe(urun_id="Satın alınacak ürünün ID'si")
293
+ async def satinal(self, ctx: commands.Context, urun_id: int):
294
+ if ctx.interaction:
295
+ await ctx.defer()
296
+ item = await Database.fetch_one(
297
+ "SELECT * FROM ekonomi_magaza WHERE urun_id = ? AND guild_id = ?",
298
+ (urun_id, ctx.guild.id)
299
+ )
300
+ if not item:
301
+ await ctx.send("❌ Ürün bulunamadı.", ephemeral=True)
302
+ return
303
+
304
+ user = await self._get_user(ctx.guild.id, ctx.author.id)
305
+ if user['bakiye'] < item['fiyat']:
306
+ await ctx.send("❌ Yeterli paran yok.", ephemeral=True)
307
+ return
308
+
309
+ # Stok kontrolü
310
+ if item['stok'] != -1 and item['stok'] <= 0:
311
+ await ctx.send("❌ Ürün stokta kalmadı.", ephemeral=True)
312
+ return
313
+
314
+ await Database.execute(
315
+ "UPDATE ekonomi SET bakiye = bakiye - ? WHERE guild_id = ? AND user_id = ?",
316
+ (item['fiyat'], ctx.guild.id, ctx.author.id)
317
+ )
318
+
319
+ if item['stok'] != -1:
320
+ await Database.execute(
321
+ "UPDATE ekonomi_magaza SET stok = stok - 1 WHERE urun_id = ?",
322
+ (urun_id,)
323
+ )
324
+
325
+ if item['rol_id']:
326
+ rol = ctx.guild.get_role(item['rol_id'])
327
+ if rol:
328
+ try:
329
+ await ctx.author.add_roles(rol, reason="Mağaza satın alımı")
330
+ except:
331
+ pass
332
+
333
+ await Database.execute(
334
+ "INSERT OR IGNORE INTO ekonomi_envanter (guild_id, user_id, urun_id, adet) VALUES (?, ?, ?, 1) "
335
+ "ON CONFLICT(guild_id, user_id, urun_id) DO UPDATE SET adet = adet + 1",
336
+ (ctx.guild.id, ctx.author.id, urun_id)
337
+ )
338
+
339
+ await ctx.send(f"✅ **{item['isim']}** satın alındı! 🛍️")
340
+
341
+ @commands.hybrid_command(name="envanter", description="Envanterinizi gösterir.")
342
+ async def envanter(self, ctx: commands.Context):
343
+ if ctx.interaction:
344
+ await ctx.defer()
345
+ items = await Database.fetch_all(
346
+ "SELECT e.adet, m.isim FROM ekonomi_envanter e "
347
+ "JOIN ekonomi_magaza m ON e.urun_id = m.urun_id "
348
+ "WHERE e.guild_id = ? AND e.user_id = ?",
349
+ (ctx.guild.id, ctx.author.id)
350
+ )
351
+ if not items:
352
+ await ctx.send("ℹ️ Envanteriniz boş.")
353
+ return
354
+
355
+ embed = discord.Embed(title="🎒 Envanter", color=CYBER_BLUE, timestamp=datetime.now())
356
+ desc = ""
357
+ for it in items:
358
+ desc += f"• **{it['isim']}** x{it['adet']}\n"
359
+ embed.description = desc
360
+ await ctx.send(embed=embed)
361
+
362
+
363
+ async def setup(bot):
364
+ await bot.add_cog(Economy(bot))
cogs/fun.py ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ import random
5
+ import asyncio
6
+ from datetime import datetime
7
+
8
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
9
+ CYBER_ORANGE = discord.Color.from_rgb(230, 126, 34)
10
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
11
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
12
+
13
+ ESPRILER = [
14
+ "Geçen gün bir taksi çevirdim, hala dönüyor.",
15
+ "Adamın biri gülmüş, saksıya koymuşlar.",
16
+ "Rüzgar esmiş, fırtına çıkmış, ben çıkamamışım.",
17
+ "Sinemada on dakika ara verdi, ben vermedim.",
18
+ "Geçen gün bir yazı yazdım, mürekkep bitti, tükenmez kalemle devam ettim.",
19
+ "Yıkanan ton balığına ne denir? Yıkanmış ton balığı.",
20
+ "Ben yürüyelim diyorum, o yollar yürümekle aşınmaz diyor.",
21
+ "En temiz böcek hangisidir? Hamam böceği.",
22
+ "Soru: Adamın biri uçurumdan atlayacakmış, vazgeçmiş neden? Çünkü vazgeçmiş.",
23
+ "Su içmek ücretsiz, ama su faturası ücretli. Hayat paradoks.",
24
+ "İki sıfır bir görüşmüş, üç olmak için.",
25
+ "Çöpçü olmak istiyorum, hayatta bir şeyleri çöp etmek için."
26
+ ]
27
+
28
+ TRIVIA_QUESTIONS = [
29
+ {"q": "Türkiye Cumhuriyeti'nin kurucusu kimdir?", "a": "atatürk"},
30
+ {"q": "Gökyüzü neden mavi görünür?", "a": "saçılma"},
31
+ {"q": "Dünyanın en yüksek dağı hangisidir?", "a": "everest"},
32
+ {"q": "Suyu oluşturan hidrojen ve hangi elementtir?", "a": "oksijen"},
33
+ {"q": "İstiklal Marşı'mızın şairi kimdir?", "a": "mehmet akif"},
34
+ {"q": "İstanbul hangi yıl fethedilmiştir?", "a": "1453"},
35
+ {"q": "Kız Kulesi hangi şehrimizdedir?", "a": "istanbul"},
36
+ {"q": "Dünya Güneş etrafındaki dönüşünü kaç günde tamamlar?", "a": "365"},
37
+ {"q": "Python programlama dilini kim yarattı?", "a": "guido"},
38
+ {"q": "Dünyanın en büyük okyanusu hangisidir?", "a": "pasifik"},
39
+ {"q": "Discord hangi yılda kurulmuştur?", "a": "2015"},
40
+ {"q": "Pi sayısının ilk üç hanesi nedir?", "a": "3.14"},
41
+ {"q": "Türkiye'nin başkenti neresidir?", "a": "ankara"},
42
+ {"q": "HTML neyin kısaltmasıdır?", "a": "hypertext"},
43
+ {"q": "Avrupa'nın en büyük ülkesi hangisidir?", "a": "rusya"},
44
+ ]
45
+
46
+ # Aktif trivia kanallarını takip et (aynı anda birden fazla olmasın)
47
+ ACTIVE_TRIVIA = set()
48
+
49
+
50
+ class Fun(commands.Cog):
51
+ def __init__(self, bot):
52
+ self.bot = bot
53
+
54
+ @commands.command(name="yazıtura", description="Yazı mı tura mı? Parayı fırlatır.")
55
+ async def yazitura(self, ctx: commands.Context):
56
+ result = random.choice(["Yazı ✍️", "Tura 🔵"])
57
+ embed = discord.Embed(
58
+ title="🪙 Yazı Tura",
59
+ description=f"{ctx.author.mention} parayı fırlatıyor...\n\n⏳ *Para havada dönüyor...*",
60
+ color=CYBER_ORANGE
61
+ )
62
+ msg = await ctx.send(embed=embed)
63
+ await asyncio.sleep(1.5)
64
+ embed.description = f"{ctx.author.mention} parayı fırlattı!\n\n🪙 Sonuç: **{result}**"
65
+ await msg.edit(embed=embed)
66
+
67
+ @commands.command(name="zar-at", description="6 yüzlü bir zar atar.")
68
+ async def zar_at(self, ctx: commands.Context):
69
+ zar_gorselleri = {1: "⚀", 2: "⚁", 3: "⚂", 4: "⚃", 5: "⚄", 6: "⚅"}
70
+ zar = random.randint(1, 6)
71
+ embed = discord.Embed(
72
+ title="🎲 Zar Atıldı",
73
+ description=f"{ctx.author.mention} zarı attı!\n\n{zar_gorselleri[zar]} **{zar}** geldi!",
74
+ color=CYBER_BLUE
75
+ )
76
+ await ctx.send(embed=embed)
77
+
78
+ @commands.command(name="aşk-ölçer", description="İki üye arasındaki aşk uyumunu ölçer.")
79
+ async def ask_olcer(self, ctx: commands.Context, uye1: discord.Member, uye2: discord.Member = None):
80
+ if uye2 is None:
81
+ uye2 = ctx.author
82
+
83
+ oran = random.randint(0, 100)
84
+
85
+ if oran < 30:
86
+ mesaj = "💔 Pek umut yok gibi duruyor, arkadaş kalmanız daha hayırlı."
87
+ elif oran < 60:
88
+ mesaj = "💛 İdare eder bir çekim var ama çabalamanız lazım."
89
+ elif oran < 85:
90
+ mesaj = "💖 Harika bir uyum! Gelecek vadediyor."
91
+ else:
92
+ mesaj = "💞 Efsanevi Aşk! Bir elmanın iki yarısı gibisiniz!"
93
+
94
+ dolu_blok = int(oran / 10)
95
+ bos_blok = 10 - dolu_blok
96
+ cubuk = "❤️" * dolu_blok + "🖤" * bos_blok
97
+
98
+ embed = discord.Embed(
99
+ title="💘 Aşk Ölçer",
100
+ description=(
101
+ f"💘 **{uye1.display_name}** ile **{uye2.display_name}** arasındaki aşk oranı:\n\n"
102
+ f"**Oran:** `%{oran}`\n"
103
+ f"{cubuk}\n\n"
104
+ f"{mesaj}"
105
+ ),
106
+ color=discord.Color.from_rgb(231, 76, 60),
107
+ timestamp=datetime.now()
108
+ )
109
+ await ctx.send(embed=embed)
110
+
111
+ @commands.command(name="slot", description="Şansınızı meyve slot makinesinde deneyin.")
112
+ async def slot(self, ctx: commands.Context):
113
+ emoji_list = ["🍎", "🍋", "🍇", "🍒", "💎", "⭐", "🍉", "🔔"]
114
+ slot1 = random.choice(emoji_list)
115
+ slot2 = random.choice(emoji_list)
116
+ slot3 = random.choice(emoji_list)
117
+
118
+ if slot1 == slot2 == slot3:
119
+ status = "🎉 **TEBRİKLER! JACKPOT!** 🎉"
120
+ color = CYBER_GREEN
121
+ elif slot1 == slot2 or slot2 == slot3 or slot1 == slot3:
122
+ status = "✨ **Çok yakındı! Amorti aldınız!** ✨"
123
+ color = CYBER_BLUE
124
+ else:
125
+ status = "❌ **Kaybettiniz. Tekrar deneyin!** ❌"
126
+ color = CYBER_RED
127
+
128
+ embed = discord.Embed(
129
+ title="🎰 Slot Makinesi",
130
+ description="🎰 Makine dönüyor...\n\n`[ ⌛ | ⌛ | ⌛ ]`",
131
+ color=CYBER_ORANGE
132
+ )
133
+ msg = await ctx.send(embed=embed)
134
+ await asyncio.sleep(1.5)
135
+ embed.title = "🎰 Slot Sonucu"
136
+ embed.description = f"{ctx.author.mention} slot çevirdi:\n\n`[ {slot1} | {slot2} | {slot3} ]`\n\n{status}"
137
+ embed.color = color
138
+ await msg.edit(embed=embed)
139
+
140
+ @commands.command(name="espri", description="Komik/soğuk bir espri yapar.")
141
+ async def espri(self, ctx: commands.Context):
142
+ random_espri = random.choice(ESPRILER)
143
+ embed = discord.Embed(
144
+ title="😂 Günün Esprisi",
145
+ description=f"*{random_espri}*",
146
+ color=CYBER_ORANGE
147
+ )
148
+ await ctx.send(embed=embed)
149
+
150
+ @commands.command(name="bilgi-yarışması", description="Genel kültür bilgi yarışması başlatır.")
151
+ async def bilgi_yarismasi(self, ctx: commands.Context):
152
+ channel_id = ctx.channel.id
153
+
154
+ # Aynı kanalda zaten aktif bir yarışma var mı kontrol et
155
+ if channel_id in ACTIVE_TRIVIA:
156
+ await ctx.send("⚠️ Bu kanalda zaten devam eden bir bilgi yarışması var! Önce bitmesini bekleyin.")
157
+ return
158
+
159
+ ACTIVE_TRIVIA.add(channel_id)
160
+ question_data = random.choice(TRIVIA_QUESTIONS)
161
+ correct_answer = question_data['a']
162
+
163
+ try:
164
+ embed = discord.Embed(
165
+ title="🧠 Bilgi Yarışması Başladı!",
166
+ description=(
167
+ f"**Soru:** {question_data['q']}\n\n"
168
+ f"⏱️ *Cevap vermek için 30 saniyeniz var!*\n"
169
+ f"💡 *Cevabınızı buraya yazın.*"
170
+ ),
171
+ color=CYBER_BLUE,
172
+ timestamp=datetime.now()
173
+ )
174
+ embed.set_footer(text="ArazAI Bot • Bilgi Yarışması")
175
+
176
+ await ctx.send(embed=embed)
177
+
178
+ # Cevabı bekle - her zaman ctx.channel.send kullan (interaction sorunu yok)
179
+ def check(m: discord.Message):
180
+ return (
181
+ m.channel.id == channel_id
182
+ and not m.author.bot
183
+ and correct_answer.lower() in m.content.lower()
184
+ )
185
+
186
+ try:
187
+ msg = await self.bot.wait_for("message", check=check, timeout=30.0)
188
+
189
+ success_embed = discord.Embed(
190
+ title="🏆 Doğru Cevap Verildi!",
191
+ description=(
192
+ f"Tebrikler {msg.author.mention}! 🎉\n\n"
193
+ f"**Soru:** {question_data['q']}\n"
194
+ f"**Doğru Cevap:** {question_data['a'].title()}"
195
+ ),
196
+ color=CYBER_GREEN,
197
+ timestamp=datetime.now()
198
+ )
199
+ success_embed.set_thumbnail(url=msg.author.display_avatar.url)
200
+ await ctx.channel.send(embed=success_embed)
201
+
202
+ except asyncio.TimeoutError:
203
+ timeout_embed = discord.Embed(
204
+ title="⏰ Süre Doldu!",
205
+ description=(
206
+ f"Kimse doğru cevabı veremedi. 😔\n\n"
207
+ f"**Soru:** {question_data['q']}\n"
208
+ f"**Doğru Cevap:** {question_data['a'].title()}"
209
+ ),
210
+ color=CYBER_RED,
211
+ timestamp=datetime.now()
212
+ )
213
+ await ctx.channel.send(embed=timeout_embed)
214
+
215
+ finally:
216
+ # Yarışma her durumda aktif listeden kaldırılsın
217
+ ACTIVE_TRIVIA.discard(channel_id)
218
+
219
+ @commands.command(name="avatar", description="Belirtilen üyenin avatarını büyük boyutta gösterir.")
220
+ async def avatar(self, ctx: commands.Context, member: discord.Member = None):
221
+ member = member or ctx.author
222
+ embed = discord.Embed(
223
+ title=f"🖼️ {member.display_name} Avatarı",
224
+ color=CYBER_BLUE
225
+ )
226
+ embed.set_image(url=member.display_avatar.url)
227
+ embed.set_footer(text=f"İstekte bulunan: {ctx.author.display_name}")
228
+ await ctx.send(embed=embed)
229
+
230
+ @commands.command(name="8top", description="Sihirli 8 top ile sorunuzu yanıtlar.")
231
+ async def sekiz_top(self, ctx: commands.Context, *, soru: str):
232
+ cevaplar = [
233
+ "✅ Kesinlikle evet!", "✅ Buna kesin olarak güvenebilirsin.",
234
+ "✅ Evet, şüphesiz.", "✅ Görünüşe göre evet.",
235
+ "🤔 Cevabım belirsiz, tekrar dene.", "🤔 Şimdi tahmin edemiyorum.",
236
+ "🤔 Şimdi sorma, daha sonra sor.", "🤔 Daha fazla konsantre ol ve tekrar sor.",
237
+ "❌ Buna güvenme.", "❌ Cevabım hayır.", "❌ Görünüşe göre hayır.", "❌ Kesinlikle hayır."
238
+ ]
239
+ cevap = random.choice(cevaplar)
240
+ embed = discord.Embed(
241
+ title="🎱 Sihirli 8 Top",
242
+ description=f"**Soru:** {soru}\n\n**Cevap:** {cevap}",
243
+ color=CYBER_BLUE
244
+ )
245
+ await ctx.send(embed=embed)
246
+
247
+
248
+ async def setup(bot):
249
+ await bot.add_cog(Fun(bot))
cogs/giveaway.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands, tasks
3
+ from discord import app_commands
4
+ from datetime import datetime, timedelta
5
+ from database import Database
6
+
7
+ CYBER_GOLD = discord.Color.from_rgb(241, 196, 15)
8
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
9
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
10
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
11
+
12
+
13
+ class Giveaway(commands.Cog):
14
+ def __init__(self, bot):
15
+ self.bot = bot
16
+ self.giveaway_loop.start()
17
+ self._giveaway_views_added = False
18
+
19
+ def cog_unload(self):
20
+ self.giveaway_loop.cancel()
21
+
22
+ @tasks.loop(seconds=10)
23
+ async def giveaway_loop(self):
24
+ try:
25
+ aktif_cekilisler = await Database.fetch_all("SELECT * FROM cekilisler WHERE aktif = 1")
26
+ now = datetime.now()
27
+ for cekilis in aktif_cekilisler:
28
+ bitis = datetime.fromisoformat(cekilis['bitis_zamani'])
29
+ if now >= bitis:
30
+ await self._end_giveaway(cekilis)
31
+ except Exception as e:
32
+ print(f"Giveaway loop hatası: {e}")
33
+
34
+ @giveaway_loop.before_loop
35
+ async def before_giveaway_loop(self):
36
+ await self.bot.wait_until_ready()
37
+
38
+ async def _end_giveaway(self, cekilis):
39
+ try:
40
+ guild = self.bot.get_guild(cekilis['guild_id'])
41
+ if not guild:
42
+ return
43
+ channel = guild.get_channel(cekilis['kanal_id'])
44
+ if not channel:
45
+ return
46
+ try:
47
+ message = await channel.fetch_message(cekilis['mesaj_id'])
48
+ except:
49
+ return
50
+
51
+ katilimcilar = await Database.fetch_all(
52
+ "SELECT user_id FROM cekilis_katilimcilar WHERE mesaj_id = ?",
53
+ (cekilis['mesaj_id'],)
54
+ )
55
+
56
+ await Database.execute(
57
+ "UPDATE cekilisler SET aktif = 0 WHERE mesaj_id = ?",
58
+ (cekilis['mesaj_id'],)
59
+ )
60
+
61
+ if not katilimcilar:
62
+ embed = message.embeds[0] if message.embeds else None
63
+ if embed:
64
+ embed.color = CYBER_RED
65
+ embed.title = f"🎉 Çekiliş Bitti - {cekilis['odul']}"
66
+ embed.description = "❌ Çekilişe kimse katılmadı!"
67
+ await message.edit(embed=embed, view=None)
68
+ return
69
+
70
+ import random
71
+ kazanan_sayi = cekilis['kazsayi']
72
+ user_ids = [k['user_id'] for k in katilimcilar]
73
+ kazananlar = random.sample(user_ids, min(kazanan_sayi, len(user_ids)))
74
+
75
+ embed = message.embeds[0] if message.embeds else None
76
+ if embed:
77
+ embed.color = CYBER_GREEN
78
+ embed.title = f"🎉 Çekiliş Bitti - {cekilis['odul']}"
79
+ kazananlar_str = ", ".join([f"<@{uid}>" for uid in kazananlar])
80
+ embed.description = f"**Kazanan(lar):** {kazananlar_str}\n\n🎉 Tebrikler!"
81
+ await message.edit(embed=embed, view=None)
82
+
83
+ await channel.send(f"🎉 Çekiliş bitti! **{cekilis['odul']}** kazananı: {', '.join([f'<@{uid}>' for uid in kazananlar])} Tebrikler! 🎊")
84
+
85
+ except Exception as e:
86
+ print(f"Giveaway bitirme hatası: {e}")
87
+
88
+ @commands.hybrid_command(name="çekiliş-başlat", description="Yeni bir çekiliş başlatır.")
89
+ @commands.has_permissions(manage_guild=True)
90
+ @app_commands.describe(
91
+ odul="Çekiliş ödülü",
92
+ sure="Süre (örn: 10s, 5m, 2h, 1d, 1w)",
93
+ kazanan_sayisi="Kaç kazanan olacağı",
94
+ kanal="Çekiliş mesajının gönderileceği kanal (boş = mevcut kanal)"
95
+ )
96
+ async def cekilis_baslat(
97
+ self, ctx: commands.Context,
98
+ odul: str,
99
+ sure: str,
100
+ kazanan_sayisi: int = 1,
101
+ kanal: discord.TextChannel = None
102
+ ):
103
+ if ctx.interaction:
104
+ await ctx.defer()
105
+ try:
106
+ delta = parse_giveaway_duration(sure)
107
+ except ValueError as e:
108
+ await ctx.send(f"❌ {e}", ephemeral=True)
109
+ return
110
+
111
+ if kazanan_sayisi < 1:
112
+ kazanan_sayisi = 1
113
+
114
+ hedef_kanal = kanal or ctx.channel
115
+ bitis = datetime.now() + delta
116
+
117
+ embed = discord.Embed(
118
+ title=f"🎉 Çekiliş - {odul}",
119
+ description=(
120
+ f"**Ödül:** {odul}\n"
121
+ f"**Kazanan Sayısı:** {kazanan_sayisi}\n"
122
+ f"**Bitiş:** <t:{int(bitis.timestamp())}:R> (<t:{int(bitis.timestamp())}:F>)\n\n"
123
+ f"Aşağıdaki butona tıklayarak katıl! 🍀"
124
+ ),
125
+ color=CYBER_GOLD,
126
+ timestamp=datetime.now()
127
+ )
128
+ embed.set_footer(text=f"Başlatan: {ctx.author} | Katılımcı: 0")
129
+ embed.set_thumbnail(url=ctx.guild.icon.url if ctx.guild.icon else "")
130
+
131
+ # Geçici bir dummy mesaj_id ile view oluştur
132
+ msg = await hedef_kanal.send(embed=embed)
133
+ view = discord.ui.View(timeout=None)
134
+ view.add_item(discord.ui.Button(
135
+ label="🎉 Katıl",
136
+ style=discord.ButtonStyle.success,
137
+ custom_id=f"giveaway_join_{msg.id}"
138
+ ))
139
+ await msg.edit(view=view)
140
+
141
+ await Database.execute(
142
+ """INSERT OR REPLACE INTO cekilisler
143
+ (mesaj_id, kanal_id, guild_id, baslatan_id, odul, sure, kazsayi, bitis_zamani, aktif)
144
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1)""",
145
+ (msg.id, hedef_kanal.id, ctx.guild.id, ctx.author.id, odul, int(delta.total_seconds()), kazanan_sayisi, bitis.isoformat())
146
+ )
147
+
148
+ await ctx.send(f"✅ Çekiliş başlatıldı! {msg.jump_url}", ephemeral=True if ctx.interaction else False)
149
+
150
+ @commands.hybrid_command(name="çekiliş-bitir", description="Aktif bir çekilişi hemen bitirir.")
151
+ @commands.has_permissions(manage_guild=True)
152
+ @app_commands.describe(mesaj_id="Çekiliş mesajının ID'si")
153
+ async def cekilis_bitir(self, ctx: commands.Context, mesaj_id: str):
154
+ if ctx.interaction:
155
+ await ctx.defer()
156
+ try:
157
+ mid = int(mesaj_id)
158
+ except:
159
+ await ctx.send("❌ Geçersiz mesaj ID.", ephemeral=True)
160
+ return
161
+
162
+ cekilis = await Database.fetch_one("SELECT * FROM cekilisler WHERE mesaj_id = ? AND aktif = 1", (mid,))
163
+ if not cekilis:
164
+ await ctx.send("❌ Aktif çekiliş bulunamadı.", ephemeral=True)
165
+ return
166
+
167
+ await self._end_giveaway(cekilis)
168
+ await ctx.send("✅ Çekiliş bitirildi.")
169
+
170
+ @commands.hybrid_command(name="çekiliş-liste", description="Sunucudaki tüm çekilişleri listeler.")
171
+ async def cekilis_liste(self, ctx: commands.Context):
172
+ if ctx.interaction:
173
+ await ctx.defer()
174
+ records = await Database.fetch_all(
175
+ "SELECT * FROM cekilisler WHERE guild_id = ? ORDER BY rowid DESC LIMIT 10",
176
+ (ctx.guild.id,)
177
+ )
178
+ if not records:
179
+ await ctx.send("ℹ️ Sunucuda çekiliş bulunmamaktadır.")
180
+ return
181
+
182
+ embed = discord.Embed(title="🎉 Çekiliş Listesi", color=CYBER_GOLD, timestamp=datetime.now())
183
+ desc = ""
184
+ for r in records:
185
+ durum = "✅ Aktif" if r['aktif'] else "❌ Bitti"
186
+ bitis = datetime.fromisoformat(r['bitis_zamani']).strftime("%d.%m.%Y %H:%M")
187
+ desc += f"• **{r['odul']}** | {durum} | Bitiş: `{bitis}` | [Mesaj](https://discord.com/channels/{ctx.guild.id}/{r['kanal_id']}/{r['mesaj_id']})\n"
188
+ embed.description = desc
189
+ await ctx.send(embed=embed)
190
+
191
+ @commands.Cog.listener()
192
+ async def on_interaction(self, interaction: discord.Interaction):
193
+ if interaction.type != discord.InteractionType.component:
194
+ return
195
+ custom_id = interaction.data.get("custom_id", "")
196
+ if not custom_id.startswith("giveaway_join_"):
197
+ return
198
+
199
+ if not await Database.modul_aktif_mi(interaction.guild_id, "Giveaway"):
200
+ try:
201
+ await interaction.response.send_message("❌ Çekiliş modülü şu an devre dışı.", ephemeral=True)
202
+ except:
203
+ pass
204
+ return
205
+
206
+ try:
207
+ mesaj_id = int(custom_id.split("giveaway_join_")[-1])
208
+ except:
209
+ return
210
+
211
+ cekilis = await Database.fetch_one("SELECT * FROM cekilisler WHERE mesaj_id = ? AND aktif = 1", (mesaj_id,))
212
+ if not cekilis:
213
+ await interaction.response.send_message("❌ Bu çekiliş artık aktif değil.", ephemeral=True)
214
+ return
215
+
216
+ existing = await Database.fetch_one(
217
+ "SELECT 1 FROM cekilis_katilimcilar WHERE mesaj_id = ? AND user_id = ?",
218
+ (mesaj_id, interaction.user.id)
219
+ )
220
+ if existing:
221
+ await Database.execute(
222
+ "DELETE FROM cekilis_katilimcilar WHERE mesaj_id = ? AND user_id = ?",
223
+ (mesaj_id, interaction.user.id)
224
+ )
225
+ await interaction.response.send_message("✅ Çekilişten ayrıldın.", ephemeral=True)
226
+ else:
227
+ await Database.execute(
228
+ "INSERT OR IGNORE INTO cekilis_katilimcilar (mesaj_id, user_id) VALUES (?, ?)",
229
+ (mesaj_id, interaction.user.id)
230
+ )
231
+ await interaction.response.send_message("✅ Çekilişe katıldın! Bol şans! 🍀", ephemeral=True)
232
+
233
+ # Embed'deki katılımcı sayısını güncelle
234
+ try:
235
+ katilimci_sayi = await Database.fetch_one(
236
+ "SELECT COUNT(*) as toplam FROM cekilis_katilimcilar WHERE mesaj_id = ?",
237
+ (mesaj_id,)
238
+ )
239
+ count = katilimci_sayi['toplam'] if katilimci_sayi else 0
240
+ message = interaction.message
241
+ if message and message.embeds:
242
+ embed = message.embeds[0]
243
+ baslatan = embed.footer.text.split("|")[0].strip() if embed.footer.text else ""
244
+ embed.set_footer(text=f"{baslatan} | Katılımcı: {count}")
245
+ await message.edit(embed=embed)
246
+ except:
247
+ pass
248
+
249
+
250
+ async def setup(bot):
251
+ await bot.add_cog(Giveaway(bot))
cogs/itibar.py ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands, tasks
3
+ from discord import app_commands
4
+ from database import Database
5
+ from datetime import datetime
6
+
7
+ CYBER_GOLD = discord.Color.from_rgb(241, 196, 15)
8
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
9
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
10
+
11
+ class PopulerlikNotificationToggleView(discord.ui.View):
12
+ def __init__(self):
13
+ super().__init__(timeout=None)
14
+
15
+ @discord.ui.button(label="Bildirimleri Aç/Kapat 🔔", style=discord.ButtonStyle.primary, custom_id="toggle_populerlik_notif")
16
+ async def toggle_notif(self, interaction: discord.Interaction, button: discord.ui.Button):
17
+ row = await Database.fetch_one(
18
+ "SELECT aktif FROM itibar_bildirim_tercihleri WHERE guild_id = ? AND user_id = ?",
19
+ (interaction.guild.id, interaction.user.id)
20
+ )
21
+
22
+ current_state = row['aktif'] if row else 1
23
+ new_state = 0 if current_state == 1 else 1
24
+
25
+ await Database.execute(
26
+ "INSERT OR REPLACE INTO itibar_bildirim_tercihleri (guild_id, user_id, aktif) VALUES (?, ?, ?)",
27
+ (interaction.guild.id, interaction.user.id, new_state)
28
+ )
29
+
30
+ durum_str = "✅ **Aktif Edildi** (Popülerlik gönderme süreniz dolduğunda etiketleneceksiniz)." if new_state == 1 else "❌ **Kapatıldı** (Artık bildirim almayacaksınız)."
31
+ await interaction.response.send_message(f"🔔 Popülerlik bildirim tercihiniz: {durum_str}", ephemeral=True)
32
+
33
+
34
+ class Itibar(commands.Cog):
35
+ def __init__(self, bot):
36
+ self.bot = bot
37
+ self._cooldowns = {} # (guild_id, giver_id, target_id) -> datetime
38
+ self._last_given = {} # giver_id -> target_id (üst üste aynı kişiye vermeyi engellemek için)
39
+ self.cooldown_check_loop.start()
40
+
41
+ def cog_unload(self):
42
+ self.cooldown_check_loop.cancel()
43
+
44
+ async def cog_load(self):
45
+ # Database tablolaları eski yapı ile uyumluluk için aynı kalıyor
46
+ await Database.execute("""
47
+ CREATE TABLE IF NOT EXISTS itibar (
48
+ guild_id INTEGER,
49
+ user_id INTEGER,
50
+ puan INTEGER DEFAULT 0,
51
+ PRIMARY KEY (guild_id, user_id)
52
+ )
53
+ """)
54
+ await Database.execute("""
55
+ CREATE TABLE IF NOT EXISTS itibar_roller (
56
+ guild_id INTEGER,
57
+ rol_id INTEGER,
58
+ hedef_puan INTEGER,
59
+ PRIMARY KEY (guild_id, hedef_puan)
60
+ )
61
+ """)
62
+ await Database.execute("""
63
+ CREATE TABLE IF NOT EXISTS itibar_ayarlar (
64
+ guild_id INTEGER PRIMARY KEY,
65
+ cooldown_seconds INTEGER DEFAULT 60
66
+ )
67
+ """)
68
+ await Database.execute("""
69
+ CREATE TABLE IF NOT EXISTS itibar_log_ayarlar (
70
+ guild_id INTEGER PRIMARY KEY,
71
+ kanal_id INTEGER
72
+ )
73
+ """)
74
+ await Database.execute("""
75
+ CREATE TABLE IF NOT EXISTS itibar_yasakli_kanallar (
76
+ guild_id INTEGER,
77
+ kanal_id INTEGER,
78
+ PRIMARY KEY (guild_id, kanal_id)
79
+ )
80
+ """)
81
+ await Database.execute("""
82
+ CREATE TABLE IF NOT EXISTS itibar_bildirim_ayarlar (
83
+ guild_id INTEGER PRIMARY KEY,
84
+ kanal_id INTEGER
85
+ )
86
+ """)
87
+ await Database.execute("""
88
+ CREATE TABLE IF NOT EXISTS itibar_bildirim_tercihleri (
89
+ guild_id INTEGER,
90
+ user_id INTEGER,
91
+ aktif INTEGER DEFAULT 1,
92
+ PRIMARY KEY (guild_id, user_id)
93
+ )
94
+ """)
95
+
96
+ @tasks.loop(seconds=5)
97
+ async def cooldown_check_loop(self):
98
+ try:
99
+ now = datetime.now()
100
+ expired_keys = []
101
+
102
+ for key, last_used in list(self._cooldowns.items()):
103
+ guild_id, giver_id, target_id = key
104
+
105
+ cooldown_row = await Database.fetch_one("SELECT cooldown_seconds FROM itibar_ayarlar WHERE guild_id = ?", (guild_id,))
106
+ cooldown_seconds = cooldown_row['cooldown_seconds'] if cooldown_row else 60
107
+
108
+ if (now - last_used).total_seconds() >= cooldown_seconds:
109
+ expired_keys.append(key)
110
+
111
+ notify_row = await Database.fetch_one("SELECT kanal_id FROM itibar_bildirim_ayarlar WHERE guild_id = ?", (guild_id,))
112
+ if notify_row:
113
+ pref_row = await Database.fetch_one("SELECT aktif FROM itibar_bildirim_tercihleri WHERE guild_id = ? AND user_id = ?", (guild_id, giver_id))
114
+ is_active = pref_row['aktif'] if pref_row else 1
115
+
116
+ if is_active == 1:
117
+ guild = self.bot.get_guild(guild_id)
118
+ if guild:
119
+ channel = guild.get_channel(notify_row['kanal_id'])
120
+ giver = guild.get_member(giver_id)
121
+ target = guild.get_member(target_id)
122
+ if channel and giver and target:
123
+ try:
124
+ await channel.send(f"🔔 {giver.mention}, artık **{target.display_name}** kullanıcısına tekrar popülerlik gönderebilirsin!")
125
+ except:
126
+ pass
127
+
128
+ for key in expired_keys:
129
+ if key in self._cooldowns:
130
+ del self._cooldowns[key]
131
+ except Exception as e:
132
+ print(f"Populerlik cooldown loop hatası: {e}")
133
+
134
+ @cooldown_check_loop.before_loop
135
+ async def before_cooldown_check_loop(self):
136
+ await self.bot.wait_until_ready()
137
+
138
+ async def cog_check(self, ctx: commands.Context):
139
+ if not ctx.guild:
140
+ return True
141
+
142
+ is_active = await Database.modul_aktif_mi(ctx.guild.id, "Itibar")
143
+ if not is_active:
144
+ raise commands.CheckFailure("⚙️ **Popülerlik** modülü bu sunucuda devre dışı bırakılmıştır.")
145
+
146
+ if not ctx.author.guild_permissions.administrator:
147
+ row = await Database.fetch_one(
148
+ "SELECT kanal_id FROM itibar_yasakli_kanallar WHERE guild_id = ? AND kanal_id = ?",
149
+ (ctx.guild.id, ctx.channel.id)
150
+ )
151
+ if row:
152
+ raise commands.CheckFailure("❌ Bu kanalda popülerlik sorgulama ve sıralama komutlarını kullanmak yasaklanmıştır!")
153
+
154
+ return True
155
+
156
+ async def cog_command_error(self, ctx: commands.Context, error: Exception):
157
+ if isinstance(error, commands.CheckFailure):
158
+ try:
159
+ await ctx.send(str(error), ephemeral=True)
160
+ except:
161
+ pass
162
+
163
+ async def _send_populerlik_event_log(self, guild: discord.Guild, giver: discord.Member, receiver: discord.Member, new_puan: int):
164
+ row = await Database.fetch_one("SELECT kanal_id FROM itibar_log_ayarlar WHERE guild_id = ?", (guild.id,))
165
+ if not row:
166
+ return
167
+
168
+ log_channel = guild.get_channel(row['kanal_id'])
169
+ if log_channel:
170
+ embed = discord.Embed(
171
+ title="✨ Popülerlik Etkinliği",
172
+ description=f"Sunucuda yeni bir popülerlik puanı gönderildi!",
173
+ color=CYBER_GOLD,
174
+ timestamp=datetime.now()
175
+ )
176
+ embed.add_field(name="⭐ Gönderen", value=giver.mention, inline=True)
177
+ embed.add_field(name="🎯 Alan", value=receiver.mention, inline=True)
178
+ embed.add_field(name="📊 Yeni Puan", value=f"**{new_puan}**", inline=True)
179
+ embed.set_footer(text="ArazAI Popülerlik Takip Sistemi")
180
+ try:
181
+ await log_channel.send(embed=embed)
182
+ except:
183
+ pass
184
+
185
+ async def _check_role_rewards(self, member: discord.Member, new_puan: int):
186
+ rows = await Database.fetch_all(
187
+ "SELECT rol_id, hedef_puan FROM itibar_roller WHERE guild_id = ? ORDER BY hedef_puan ASC",
188
+ (member.guild.id,)
189
+ )
190
+ if not rows:
191
+ return
192
+
193
+ roles_to_add = []
194
+ for row in rows:
195
+ rol_id = row['rol_id']
196
+ hedef_puan = row['hedef_puan']
197
+
198
+ if new_puan >= hedef_puan:
199
+ role = member.guild.get_role(rol_id)
200
+ if role and role not in member.roles:
201
+ roles_to_add.append(role)
202
+
203
+ if roles_to_add:
204
+ try:
205
+ await member.add_roles(*roles_to_add, reason="ArazAI Popülerlik Sistemi Rol Ödülü")
206
+ channel = member.guild.system_channel or member.guild.text_channels[0]
207
+ roles_str = ", ".join([r.mention for r in roles_to_add])
208
+ embed = discord.Embed(
209
+ title="🎉 Popülerlik Rol Ödülü!",
210
+ description=f"Tebrikler {member.mention}! **{new_puan}** popülerlik puanına ulaşarak {roles_str} rolünü/rollerini kazandınız! 🏆",
211
+ color=CYBER_GOLD,
212
+ timestamp=datetime.now()
213
+ )
214
+ embed.set_thumbnail(url=member.display_avatar.url)
215
+ await channel.send(embed=embed)
216
+ except Exception as e:
217
+ print(f"Popülerlik rol ödülü verilirken hata oluştu: {e}")
218
+
219
+ @commands.Cog.listener()
220
+ async def on_message(self, message: discord.Message):
221
+ if message.author.bot or not message.guild:
222
+ return
223
+
224
+ if not await Database.modul_aktif_mi(message.guild.id, "Itibar"):
225
+ return
226
+
227
+ content = message.content.strip().lower()
228
+ words = content.split()
229
+ is_rep_trigger = "!1" in words
230
+ target_member = None
231
+
232
+ if is_rep_trigger and message.reference and message.reference.resolved:
233
+ resolved_msg = message.reference.resolved
234
+ if isinstance(resolved_msg, discord.Message):
235
+ target_member = resolved_msg.author
236
+
237
+ elif is_rep_trigger and message.mentions:
238
+ for m in message.mentions:
239
+ if m.id != message.author.id and not m.bot:
240
+ target_member = m
241
+ break
242
+
243
+ if target_member:
244
+ if target_member.id == message.author.id:
245
+ try:
246
+ await message.reply("❌ Kendinize popülerlik gönderemezsiniz!", delete_after=5)
247
+ except:
248
+ pass
249
+ return
250
+
251
+ if target_member.bot:
252
+ try:
253
+ await message.reply("❌ Botlara popülerlik gönderemezsiniz!", delete_after=5)
254
+ except:
255
+ pass
256
+ return
257
+
258
+ if self._last_given.get(message.author.id) == target_member.id:
259
+ try:
260
+ await message.reply("❌ Aynı kişiye **üst üste** popülerlik gönderemezsiniz! Araya başka birine puan göndermeniz gerekir.", delete_after=5)
261
+ except:
262
+ pass
263
+ return
264
+
265
+ cooldown_row = await Database.fetch_one("SELECT cooldown_seconds FROM itibar_ayarlar WHERE guild_id = ?", (message.guild.id,))
266
+ cooldown_seconds = cooldown_row['cooldown_seconds'] if cooldown_row else 60
267
+
268
+ now = datetime.now()
269
+ cooldown_key = (message.guild.id, message.author.id, target_member.id)
270
+ last_used = self._cooldowns.get(cooldown_key)
271
+ if last_used and (now - last_used).total_seconds() < cooldown_seconds:
272
+ left = cooldown_seconds - int((now - last_used).total_seconds())
273
+ try:
274
+ await message.reply(f"⏳ Bu kullanıcıya tekrar popülerlik göndermek için **{left}** saniye beklemelisiniz.", delete_after=5)
275
+ except:
276
+ pass
277
+ return
278
+
279
+ self._cooldowns[cooldown_key] = now
280
+ self._last_given[message.author.id] = target_member.id
281
+
282
+ row = await Database.fetch_one("SELECT puan FROM itibar WHERE guild_id = ? AND user_id = ?", (message.guild.id, target_member.id))
283
+ current_puan = (row['puan'] if row else 0) + 1
284
+ await Database.execute("INSERT OR REPLACE INTO itibar (guild_id, user_id, puan) VALUES (?, ?, ?)", (message.guild.id, target_member.id, current_puan))
285
+
286
+ embed = discord.Embed(
287
+ description=f"⭐ {message.author.mention}, **{target_member.mention}** kullanıcısına popülerlik gönderdi!\n\n✨ Yeni Popülerlik Puanı: **{current_puan}**",
288
+ color=CYBER_GOLD
289
+ )
290
+ await message.reply(embed=embed, mention_author=False)
291
+
292
+ await self._send_populerlik_event_log(message.guild, message.author, target_member, current_puan)
293
+ await self._check_role_rewards(target_member, current_puan)
294
+
295
+ @commands.hybrid_command(name="popülerlik", description="Kendinizin veya başka bir üyenin popülerlik puanını gösterir.")
296
+ @app_commands.describe(uye="Popülerlik puanına bakılacak üye veya 'top'")
297
+ async def populerlik(self, ctx: commands.Context, *, uye: str = None):
298
+ if ctx.interaction:
299
+ await ctx.defer()
300
+
301
+ if uye and uye.lower().strip() == "top":
302
+ await self.populerlik_siralanis(ctx)
303
+ return
304
+
305
+ target_member = ctx.author
306
+ if uye:
307
+ try:
308
+ converter = commands.MemberConverter()
309
+ target_member = await converter.convert(ctx, uye)
310
+ except commands.MemberNotFound:
311
+ await ctx.send("❌ Belirtilen üye bulunamadı!", ephemeral=True)
312
+ return
313
+
314
+ row = await Database.fetch_one("SELECT puan FROM itibar WHERE guild_id = ? AND user_id = ?", (ctx.guild.id, target_member.id))
315
+ puan = row['puan'] if row else 0
316
+
317
+ embed = discord.Embed(
318
+ title=f"⭐ Popülerlik Profili: {target_member.display_name}",
319
+ description=f"{target_member.mention} kullanıcısının toplam **{puan}** popülerlik puanı bulunmaktadır.",
320
+ color=CYBER_GOLD,
321
+ timestamp=datetime.now()
322
+ )
323
+ embed.set_thumbnail(url=target_member.display_avatar.url)
324
+ embed.set_footer(text=f"Sorgulayan: {ctx.author.display_name} • ArazAI")
325
+ await ctx.send(embed=embed)
326
+
327
+ @commands.hybrid_command(name="popülerlik-sıralaması", aliases=["popülerlik-top", "popülerliktop", "popülerlik_top"], description="Sunucudaki en yüksek popülerliğe sahip üyeleri listeler.")
328
+ async def populerlik_siralanis(self, ctx: commands.Context):
329
+ if ctx.interaction:
330
+ await ctx.defer()
331
+
332
+ rows = await Database.fetch_all("SELECT user_id, puan FROM itibar WHERE guild_id = ? ORDER BY puan DESC LIMIT 10", (ctx.guild.id,))
333
+ if not rows:
334
+ await ctx.send("📝 Bu sunucuda henüz hiç kimse popülerlik puanı kazanmamış.")
335
+ return
336
+
337
+ embed = discord.Embed(
338
+ title="🏆 Sunucu Popülerlik Sıralaması",
339
+ color=CYBER_GOLD,
340
+ timestamp=datetime.now()
341
+ )
342
+
343
+ leaderboard_str = ""
344
+ for index, row in enumerate(rows, 1):
345
+ user = ctx.guild.get_member(row['user_id'])
346
+ user_str = user.mention if user else f"Bilinmeyen Üye ({row['user_id']})"
347
+
348
+ medals = {1: "🥇", 2: "🥈", 3: "🥉"}
349
+ prefix = medals.get(index, f"`#{index}`")
350
+ leaderboard_str += f"{prefix} | {user_str} — **{row['puan']}** Puan\n"
351
+
352
+ embed.description = leaderboard_str
353
+ embed.set_footer(text="ArazAI Popülerlik Sistemi")
354
+ await ctx.send(embed=embed)
355
+
356
+ @commands.hybrid_command(name="popülerlik-ver", description="[Admin] Belirtilen üyeye popülerlik puanı ekler.")
357
+ @commands.has_permissions(administrator=True)
358
+ @app_commands.describe(uye="Puan eklenecek üye", miktar="Eklenecek puan miktarı")
359
+ async def populerlik_ver(self, ctx: commands.Context, uye: discord.Member, miktar: int):
360
+ if ctx.interaction:
361
+ await ctx.defer()
362
+
363
+ if miktar <= 0:
364
+ await ctx.send("❌ Eklenecek puan miktarı 0'dan büyük olmalıdır!", ephemeral=True)
365
+ return
366
+
367
+ row = await Database.fetch_one("SELECT puan FROM itibar WHERE guild_id = ? AND user_id = ?", (ctx.guild.id, uye.id))
368
+ current_puan = (row['puan'] if row else 0) + miktar
369
+ await Database.execute("INSERT OR REPLACE INTO itibar (guild_id, user_id, puan) VALUES (?, ?, ?)", (ctx.guild.id, uye.id, current_puan))
370
+
371
+ embed = discord.Embed(
372
+ title="⚙️ Popülerlik Puanı Eklendi",
373
+ description=f"**Kullanıcı:** {uye.mention}\n**Eklenen Miktar:** `+{miktar}`\n**Yeni Toplam Puan:** **{current_puan}**\n**Yetkili:** {ctx.author.mention}",
374
+ color=CYBER_GREEN,
375
+ timestamp=datetime.now()
376
+ )
377
+ embed.set_thumbnail(url=uye.display_avatar.url)
378
+ await ctx.send(embed=embed)
379
+
380
+ await self._check_role_rewards(uye, current_puan)
381
+
382
+ @commands.hybrid_command(name="popülerlik-al", description="[Admin] Belirtilen üyeden popülerlik puanı eksiltir.")
383
+ @commands.has_permissions(administrator=True)
384
+ @app_commands.describe(uye="Puanı düşürülecek üye", miktar="Düşürülecek puan miktarı")
385
+ async def populerlik_al(self, ctx: commands.Context, uye: discord.Member, miktar: int):
386
+ if ctx.interaction:
387
+ await ctx.defer()
388
+
389
+ if miktar <= 0:
390
+ await ctx.send("❌ Düşürülecek puan miktarı 0'dan büyük olmalıdır!", ephemeral=True)
391
+ return
392
+
393
+ row = await Database.fetch_one("SELECT puan FROM itibar WHERE guild_id = ? AND user_id = ?", (ctx.guild.id, uye.id))
394
+ old_puan = row['puan'] if row else 0
395
+ current_puan = max(0, old_puan - miktar)
396
+ await Database.execute("INSERT OR REPLACE INTO itibar (guild_id, user_id, puan) VALUES (?, ?, ?)", (ctx.guild.id, uye.id, current_puan))
397
+
398
+ embed = discord.Embed(
399
+ title="⚙️ Popülerlik Puanı Düşürüldü",
400
+ description=f"**Kullanıcı:** {uye.mention}\n**Eksiltilen Miktar:** `-{miktar}`\n**Yeni Toplam Puan:** **{current_puan}**\n**Yetkili:** {ctx.author.mention}",
401
+ color=CYBER_RED,
402
+ timestamp=datetime.now()
403
+ )
404
+ embed.set_thumbnail(url=uye.display_avatar.url)
405
+ await ctx.send(embed=embed)
406
+
407
+ @commands.hybrid_command(name="popülerlik-rol-ayarla", description="[Admin] Belirli bir popülerlik hedefi için verilecek rolü belirler.")
408
+ @commands.has_permissions(administrator=True)
409
+ @app_commands.describe(rol="Kazanılacak Rol", puan="Ulaşılması gereken hedef popülerlik puanı")
410
+ async def populerlik_rol_ayarla(self, ctx: commands.Context, rol: discord.Role, puan: int):
411
+ if ctx.interaction:
412
+ await ctx.defer()
413
+
414
+ if puan <= 0:
415
+ await ctx.send("❌ Hedef puan 0'dan büyük olmalıdır!", ephemeral=True)
416
+ return
417
+
418
+ await Database.execute(
419
+ "INSERT OR REPLACE INTO itibar_roller (guild_id, rol_id, hedef_puan) VALUES (?, ?, ?)",
420
+ (ctx.guild.id, rol.id, puan)
421
+ )
422
+
423
+ embed = discord.Embed(
424
+ title="⚙️ Popülerlik Rol Ödülü Ayarlandı",
425
+ description=f"**Rol:** {rol.mention}\n**Gereken Hedef Puan:** **{puan}**\n**Yetkili:** {ctx.author.mention}",
426
+ color=CYBER_GREEN,
427
+ timestamp=datetime.now()
428
+ )
429
+ await ctx.send(embed=embed)
430
+
431
+ @commands.hybrid_command(name="popülerlik-cooldown", description="[Admin] Popülerlik puanı verme bekleme süresini (saniye) ayarlar.")
432
+ @commands.has_permissions(administrator=True)
433
+ @app_commands.describe(saniye="Bekleme süresi (saniye cinsinden)")
434
+ async def populerlik_cooldown(self, ctx: commands.Context, saniye: int):
435
+ if ctx.interaction:
436
+ await ctx.defer()
437
+
438
+ if saniye < 0:
439
+ await ctx.send("❌ Bekleme süresi 0 veya daha büyük olmalıdır!", ephemeral=True)
440
+ return
441
+
442
+ await Database.execute(
443
+ "INSERT OR REPLACE INTO itibar_ayarlar (guild_id, cooldown_seconds) VALUES (?, ?)",
444
+ (ctx.guild.id, saniye)
445
+ )
446
+
447
+ embed = discord.Embed(
448
+ title="⚙️ Popülerlik Bekleme Süresi Ayarlandı",
449
+ description=f"**Yeni Bekleme Süresi:** `{saniye}` saniye\n**Yetkili:** {ctx.author.mention}",
450
+ color=CYBER_GREEN,
451
+ timestamp=datetime.now()
452
+ )
453
+ await ctx.send(embed=embed)
454
+
455
+ @commands.hybrid_command(name="popülerlik-log-ayarla", description="[Admin] Popülerlik kazanma etkinliklerinin duyurulacağı kanalı seçer.")
456
+ @commands.has_permissions(administrator=True)
457
+ @app_commands.describe(kanal="Popülerlik etkinlik log kanalı")
458
+ async def populerlik_log_ayarla(self, ctx: commands.Context, kanal: discord.TextChannel):
459
+ if ctx.interaction:
460
+ await ctx.defer()
461
+
462
+ await Database.execute(
463
+ "INSERT OR REPLACE INTO itibar_log_ayarlar (guild_id, kanal_id) VALUES (?, ?)",
464
+ (ctx.guild.id, kanal.id)
465
+ )
466
+
467
+ embed = discord.Embed(
468
+ title="⚙️ Popülerlik Log Kanalı Ayarlandı",
469
+ description=f"**Kanal:** {kanal.mention}\n**Yetkili:** {ctx.author.mention}",
470
+ color=CYBER_GREEN,
471
+ timestamp=datetime.now()
472
+ )
473
+ await ctx.send(embed=embed)
474
+
475
+ @commands.hybrid_command(name="popülerlik-kanal-yasakla", description="[Admin] Popülerlik sorgulama/sıralama komutlarının kullanımını bu kanalda yasaklar.")
476
+ @commands.has_permissions(administrator=True)
477
+ @app_commands.describe(kanal="Yasaklanacak kanal")
478
+ async def populerlik_kanal_yasakla(self, ctx: commands.Context, kanal: discord.TextChannel):
479
+ if ctx.interaction:
480
+ await ctx.defer()
481
+
482
+ await Database.execute(
483
+ "INSERT OR REPLACE INTO itibar_yasakli_kanallar (guild_id, kanal_id) VALUES (?, ?)",
484
+ (ctx.guild.id, kanal.id)
485
+ )
486
+
487
+ embed = discord.Embed(
488
+ title="🔒 Kanal Popülerlik Komutlarına Kapatıldı",
489
+ description=f"**Kanal:** {kanal.mention}\n*Not: Bu kanalda artık `!popülerlik`, `/popülerlik` ve `/popülerlik-sıralaması` kullanılamaz. Ancak `!1` ile popülerlik gönderme çalışmaya devam eder.*",
490
+ color=CYBER_RED,
491
+ timestamp=datetime.now()
492
+ )
493
+ await ctx.send(embed=embed)
494
+
495
+ @commands.hybrid_command(name="popülerlik-kanal-izin-ver", description="[Admin] Popülerlik komutları üzerindeki kanal yasağını kaldırır.")
496
+ @commands.has_permissions(administrator=True)
497
+ @app_commands.describe(kanal="Yasağı kaldırılacak kanal")
498
+ async def populerlik_kanal_izin_ver(self, ctx: commands.Context, kanal: discord.TextChannel):
499
+ if ctx.interaction:
500
+ await ctx.defer()
501
+
502
+ await Database.execute(
503
+ "DELETE FROM itibar_yasakli_kanallar WHERE guild_id = ? AND kanal_id = ?",
504
+ (ctx.guild.id, kanal.id)
505
+ )
506
+
507
+ embed = discord.Embed(
508
+ title="🔓 Kanal Popülerlik Komutlarına Açıldı",
509
+ description=f"**Kanal:** {kanal.mention}\n*Not: Bu kanalda artık popülerlik sorgulama ve sıralama komutları serbestçe kullanılabilir.*",
510
+ color=CYBER_GREEN,
511
+ timestamp=datetime.now()
512
+ )
513
+ await ctx.send(embed=embed)
514
+
515
+ @commands.hybrid_command(name="popülerlik-bildirim-kanal-ayarla", description="[Admin] Bekleme süresi dolan kullanıcıların uyarılacağı bildirim kanalını ayarlar.")
516
+ @commands.has_permissions(administrator=True)
517
+ @app_commands.describe(kanal="Süre dolum bildirim kanalı")
518
+ async def populerlik_bildirim_kanal_ayarla(self, ctx: commands.Context, kanal: discord.TextChannel):
519
+ if ctx.interaction:
520
+ await ctx.defer()
521
+
522
+ await Database.execute(
523
+ "INSERT OR REPLACE INTO itibar_bildirim_ayarlar (guild_id, kanal_id) VALUES (?, ?)",
524
+ (ctx.guild.id, kanal.id)
525
+ )
526
+
527
+ embed = discord.Embed(
528
+ title="⚙️ Popülerlik Bildirim Kanalı Ayarlandı",
529
+ description=f"**Bildirim Kanalı:** {kanal.mention}\n**Yetkili:** {ctx.author.mention}",
530
+ color=CYBER_GREEN,
531
+ timestamp=datetime.now()
532
+ )
533
+ await ctx.send(embed=embed)
534
+
535
+ @commands.hybrid_command(name="popülerlik-bildirim-paneli", description="[Admin] Üyelerin popülerlik süresi bildirimlerini açıp kapatabileceği butonlu paneli gönderir.")
536
+ @commands.has_permissions(administrator=True)
537
+ async def populerlik_bildirim_paneli(self, ctx: commands.Context):
538
+ if ctx.interaction:
539
+ await ctx.defer()
540
+
541
+ embed = discord.Embed(
542
+ title="🔔 Popülerlik Bildirim Sistemi",
543
+ description="Popülerlik gönderme bekleme süreniz dolduğunda bot tarafından etiketlenerek bildirim almak istiyorsanız aşağıdaki butonu kullanabilirsiniz.\n\n*Not: Bildirim kanalını yöneticiler `/popülerlik-bildirim-kanal-ayarla` ile belirler.*",
544
+ color=CYBER_GOLD
545
+ )
546
+
547
+ view = PopulerlikNotificationToggleView()
548
+ await ctx.send(embed=embed, view=view)
549
+
550
+ async def setup(bot):
551
+ await bot.add_cog(Itibar(bot))
cogs/level.py ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from datetime import datetime
5
+ from database import Database
6
+ import random
7
+
8
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
9
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
10
+ CYBER_GOLD = discord.Color.from_rgb(241, 196, 15)
11
+
12
+
13
+ class Level(commands.Cog):
14
+ def __init__(self, bot):
15
+ self.bot = bot
16
+ # Cooldown: {user_id: last_msg_ts}
17
+ self._xp_cooldown = {}
18
+ self.COOLDOWN_SECONDS = 30
19
+
20
+ def _xp_for_level(self, level, difficulty=100):
21
+ return 5 * (level ** 2) + 50 * level + 100 - level * difficulty // 10
22
+
23
+ def _level_from_xp(self, xp, difficulty=100):
24
+ level = 0
25
+ while xp >= self._xp_for_level(level + 1, difficulty):
26
+ level += 1
27
+ xp -= self._xp_for_level(level, difficulty)
28
+ return level, xp
29
+
30
+ async def _add_xp(self, member: discord.Member, xp_add: int):
31
+ guild_id = member.guild.id
32
+ user_id = member.id
33
+
34
+ row = await Database.fetch_one(
35
+ "SELECT * FROM seviye WHERE guild_id = ? AND user_id = ?",
36
+ (guild_id, user_id)
37
+ )
38
+ if not row:
39
+ await Database.execute(
40
+ "INSERT INTO seviye (guild_id, user_id, xp, seviye) VALUES (?, ?, ?, ?)",
41
+ (guild_id, user_id, 0, 0)
42
+ )
43
+ row = await Database.fetch_one(
44
+ "SELECT * FROM seviye WHERE guild_id = ? AND user_id = ?",
45
+ (guild_id, user_id)
46
+ )
47
+
48
+ new_xp = row['xp'] + xp_add
49
+ settings = await Database.fetch_one("SELECT * FROM seviye_ayarlar WHERE guild_id = ?", (guild_id,))
50
+ difficulty = settings['zorluk'] if settings and settings.get('zorluk') else 100
51
+
52
+ old_level = row['seviye']
53
+ new_level = self._level_from_xp(new_xp, difficulty)[0]
54
+
55
+ await Database.execute(
56
+ "UPDATE seviye SET xp = ?, seviye = ? WHERE guild_id = ? AND user_id = ?",
57
+ (new_xp, new_level, guild_id, user_id)
58
+ )
59
+
60
+ if new_level > old_level:
61
+ # Level up bildirimi
62
+ kanal_id = settings['mesaj_kanal_id'] if settings and settings.get('mesaj_kanal_id') else 0
63
+ kanal = member.guild.get_channel(kanal_id) if kanal_id else None
64
+
65
+ embed = discord.Embed(
66
+ title="🎉 Seviye Atlandı!",
67
+ description=f"Tebrikler {member.mention}! **Seviye {new_level}**'e ulaştın! 🎊",
68
+ color=CYBER_GOLD,
69
+ timestamp=datetime.now()
70
+ )
71
+
72
+ if kanal:
73
+ try:
74
+ await kanal.send(embed=embed)
75
+ except:
76
+ pass
77
+
78
+ # Rol ödülü var mı kontrol et
79
+ odul = await Database.fetch_one(
80
+ "SELECT rol_id FROM seviye_oduller WHERE guild_id = ? AND seviye = ?",
81
+ (guild_id, new_level)
82
+ )
83
+ if odul:
84
+ rol = member.guild.get_role(odul['rol_id'])
85
+ if rol:
86
+ try:
87
+ await member.add_roles(rol, reason=f"Seviye {new_level} ödülü")
88
+ except:
89
+ pass
90
+
91
+ @commands.Cog.listener()
92
+ async def on_message(self, message: discord.Message):
93
+ if message.author.bot or not message.guild:
94
+ return
95
+ if not await Database.modul_aktif_mi(message.guild.id, "Level"):
96
+ return
97
+
98
+ settings = await Database.fetch_one("SELECT aktif FROM seviye_ayarlar WHERE guild_id = ?", (message.guild.id,))
99
+ if settings and settings.get('aktif') == 0:
100
+ return
101
+
102
+ # Cooldown kontrolü
103
+ import time
104
+ now = time.time()
105
+ last = self._xp_cooldown.get(message.author.id, 0)
106
+ if now - last < self.COOLDOWN_SECONDS:
107
+ return
108
+ self._xp_cooldown[message.author.id] = now
109
+
110
+ xp_gain = random.randint(15, 25)
111
+ await self._add_xp(message.author, xp_gain)
112
+
113
+ @commands.hybrid_command(name="seviye", description="Senin veya başka bir üyenin seviye kartını gösterir.")
114
+ @app_commands.describe(uye="Seviyesine bakılacak üye")
115
+ async def seviye(self, ctx: commands.Context, uye: discord.Member = None):
116
+ if ctx.interaction:
117
+ await ctx.defer()
118
+ target = uye or ctx.author
119
+
120
+ row = await Database.fetch_one(
121
+ "SELECT * FROM seviye WHERE guild_id = ? AND user_id = ?",
122
+ (ctx.guild.id, target.id)
123
+ )
124
+ if not row:
125
+ await ctx.send(f"ℹ️ {target.mention} henüz seviye verisine sahip değil.")
126
+ return
127
+
128
+ settings = await Database.fetch_one("SELECT * FROM seviye_ayarlar WHERE guild_id = ?", (ctx.guild.id,))
129
+ difficulty = settings['zorluk'] if settings and settings.get('zorluk') else 100
130
+
131
+ level = row['seviye']
132
+ xp = row['xp']
133
+ xp_for_current = self._xp_for_level(level, difficulty)
134
+ xp_for_next = self._xp_for_level(level + 1, difficulty)
135
+ xp_needed = xp_for_next - xp_for_current
136
+ xp_progress = xp - xp_for_current
137
+ progress_pct = int((xp_progress / max(xp_needed, 1)) * 100)
138
+
139
+ bar_size = 20
140
+ filled = int((xp_progress / max(xp_needed, 1)) * bar_size)
141
+ bar = "█" * filled + "░" * (bar_size - filled)
142
+
143
+ # Sıralama hesapla
144
+ all_users = await Database.fetch_all(
145
+ "SELECT user_id, xp FROM seviye WHERE guild_id = ? ORDER BY xp DESC",
146
+ (ctx.guild.id,)
147
+ )
148
+ rank = 1
149
+ for i, u in enumerate(all_users, 1):
150
+ if u['user_id'] == target.id:
151
+ rank = i
152
+ break
153
+
154
+ embed = discord.Embed(
155
+ title=f"🏆 Seviye Kartı - {target.display_name}",
156
+ color=CYBER_GOLD,
157
+ timestamp=datetime.now()
158
+ )
159
+ embed.set_thumbnail(url=target.display_avatar.url)
160
+ embed.add_field(name="Seviye", value=f"**{level}**", inline=True)
161
+ embed.add_field(name="Toplam XP", value=f"**{xp}**", inline=True)
162
+ embed.add_field(name="Sıralama", value=f"**#{rank}**", inline=True)
163
+ embed.add_field(
164
+ name=f"İlerleme ({progress_pct}%)",
165
+ value=f"`{bar}`\n{xp_progress}/{xp_needed} XP",
166
+ inline=False
167
+ )
168
+ await ctx.send(embed=embed)
169
+
170
+ @commands.hybrid_command(name="seviye-ayarla", description="Seviye sistemi ayarlarını yapar.")
171
+ @commands.has_permissions(administrator=True)
172
+ @app_commands.describe(
173
+ mesaj_kanali="Seviye atladığında bildirim gönderilecek kanal",
174
+ zorluk="Zorluk çarpanı (örn: 100 - ne kadar yüksek o kadar yavaş ilerleme)",
175
+ aktif="Seviye sistemi aktif mi? (1=Açık, 0=Kapalı)"
176
+ )
177
+ async def seviye_ayarla(self, ctx: commands.Context, mesaj_kanali: discord.TextChannel = None, zorluk: int = 100, aktif: int = 1):
178
+ if ctx.interaction:
179
+ await ctx.defer()
180
+ kanal_id = mesaj_kanali.id if mesaj_kanali else 0
181
+ await Database.execute(
182
+ """INSERT OR REPLACE INTO seviye_ayarlar (guild_id, mesaj_kanal_id, aktif, zorluk)
183
+ VALUES (?, ?, ?, ?)""",
184
+ (ctx.guild.id, kanal_id, aktif, zorluk)
185
+ )
186
+ await ctx.send(f"✅ Seviye sistemi ayarlandı! Aktif: **{'Açık' if aktif else 'Kapalı'}**, Zorluk: **{zorluk}**")
187
+
188
+ @commands.hybrid_command(name="seviye-ödül-ekle", description="Belirli bir seviyeye ulaşınca verilecek rol ödülü ekler.")
189
+ @commands.has_permissions(administrator=True)
190
+ @app_commands.describe(seviye="Hedef seviye", rol="Verilecek rol")
191
+ async def seviye_odul_ekle(self, ctx: commands.Context, seviye: int, rol: discord.Role):
192
+ if ctx.interaction:
193
+ await ctx.defer()
194
+ await Database.execute(
195
+ "INSERT OR REPLACE INTO seviye_oduller (guild_id, seviye, rol_id) VALUES (?, ?, ?)",
196
+ (ctx.guild.id, seviye, rol.id)
197
+ )
198
+ await ctx.send(f"✅ Seviye **{seviye}**'e ulaşanlara **{rol.mention}** rolü verilecek.")
199
+
200
+ @commands.hybrid_command(name="seviye-ödül-sil", description="Bir seviye ödülünü siler.")
201
+ @commands.has_permissions(administrator=True)
202
+ @app_commands.describe(seviye="Ödül silinecek seviye")
203
+ async def seviye_odul_sil(self, ctx: commands.Context, seviye: int):
204
+ if ctx.interaction:
205
+ await ctx.defer()
206
+ await Database.execute(
207
+ "DELETE FROM seviye_oduller WHERE guild_id = ? AND seviye = ?",
208
+ (ctx.guild.id, seviye)
209
+ )
210
+ await ctx.send(f"✅ Seviye **{seviye}** ödülü silindi.")
211
+
212
+ @commands.hybrid_command(name="seviye-sıralama", description="Sunucudaki en yüksek seviyeli üyeleri gösterir.")
213
+ async def seviye_siralama(self, ctx: commands.Context):
214
+ if ctx.interaction:
215
+ await ctx.defer()
216
+ records = await Database.fetch_all(
217
+ "SELECT user_id, xp, seviye FROM seviye WHERE guild_id = ? ORDER BY xp DESC LIMIT 10",
218
+ (ctx.guild.id,)
219
+ )
220
+ if not records:
221
+ await ctx.send("ℹ️ Henüz seviye verisi bulunmamaktadır.")
222
+ return
223
+
224
+ embed = discord.Embed(title="🏆 Seviye Sıralaması", color=CYBER_GOLD, timestamp=datetime.now())
225
+ desc = ""
226
+ for i, r in enumerate(records, 1):
227
+ member = ctx.guild.get_member(r['user_id'])
228
+ name = member.mention if member else f"ID: {r['user_id']}"
229
+ emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else f"#{i}"
230
+ desc += f"{emoji} {name} — Seviye **{r['seviye']}** ({r['xp']} XP)\n"
231
+ embed.description = desc
232
+ await ctx.send(embed=embed)
233
+
234
+ @commands.hybrid_command(name="seviye-sıfırla", description="Bir üyenin seviyesini sıfırlar.")
235
+ @commands.has_permissions(administrator=True)
236
+ @app_commands.describe(uye="Seviyesi sıfırlanacak üye")
237
+ async def seviye_sifirla(self, ctx: commands.Context, uye: discord.Member):
238
+ if ctx.interaction:
239
+ await ctx.defer()
240
+ await Database.execute(
241
+ "UPDATE seviye SET xp = 0, seviye = 0 WHERE guild_id = ? AND user_id = ?",
242
+ (ctx.guild.id, uye.id)
243
+ )
244
+ await ctx.send(f"✅ {uye.mention} kullanıcısının seviyesi sıfırlandı.")
245
+
246
+
247
+ async def setup(bot):
248
+ await bot.add_cog(Level(bot))
cogs/logs.py ADDED
@@ -0,0 +1,446 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from datetime import datetime
5
+
6
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
7
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
8
+ CYBER_ORANGE = discord.Color.from_rgb(230, 126, 34)
9
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
10
+
11
+ class Logs(commands.Cog):
12
+ def __init__(self, bot):
13
+ self.bot = bot
14
+
15
+ async def _send_log(self, guild: discord.Guild, embed: discord.Embed):
16
+ """Yardımcı fonksiyon: Belirtilen log kanalına embed gönderir"""
17
+ log_channel_id = await Database.log_kanali_getir(guild.id)
18
+ if not log_channel_id:
19
+ return
20
+ channel = guild.get_channel(log_channel_id)
21
+ if channel:
22
+ try:
23
+ await channel.send(embed=embed)
24
+ except:
25
+ pass
26
+
27
+ @commands.hybrid_command(name="log-kanal-ayarla", description="Sunucu olaylarının kaydedileceği log kanalını belirler.")
28
+ @commands.has_permissions(administrator=True)
29
+ @app_commands.describe(kanal="Logların gönderileceği metin kanalı (Boş bırakılırsa log sistemi kapatılır)")
30
+ async def log_kanal_ayarla(self, ctx: commands.Context, kanal: discord.TextChannel = None):
31
+ if ctx.interaction:
32
+ await ctx.defer()
33
+
34
+ kanal_id = kanal.id if kanal else 0
35
+ try:
36
+ await Database.log_kanali_ayarla(ctx.guild.id, kanal_id)
37
+ if kanal:
38
+ await ctx.send(f"✅ Olay log kanalı {kanal.mention} olarak ayarlandı.")
39
+ else:
40
+ await ctx.send("✅ Olay loglama sistemi devre dışı bırakıldı.")
41
+ except Exception as e:
42
+ await ctx.send(f"❌ Log kanalı ayarlanırken bir veritabanı hatası oluştu: {e}", ephemeral=True)
43
+
44
+ # 1. --- SİLİNEN MESAJ LOGLARI ---
45
+ @commands.Cog.listener()
46
+ async def on_message_delete(self, message: discord.Message):
47
+ if message.author.bot or not message.guild:
48
+ return
49
+ if not await Database.modul_aktif_mi(message.guild.id, "Logs"):
50
+ return
51
+
52
+ embed = discord.Embed(
53
+ title="🗑️ Mesaj Silindi",
54
+ description=f"**Kanal:** {message.channel.mention}\n"
55
+ f"**Yazar:** {message.author.mention} (`{message.author.id}`)",
56
+ color=CYBER_RED,
57
+ timestamp=datetime.now()
58
+ )
59
+ if message.content:
60
+ embed.add_field(name="Mesaj İçeriği", value=message.content[:1024], inline=False)
61
+ else:
62
+ embed.add_field(name="Mesaj İçeriği", value="*İçerik boş veya sadece görsel/ek içeriyordu.*", inline=False)
63
+
64
+ if message.attachments:
65
+ image_url = None
66
+ for a in message.attachments:
67
+ is_image = (a.content_type and a.content_type.startswith("image")) or a.filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp'))
68
+ if is_image:
69
+ image_url = a.url
70
+ break
71
+ if image_url:
72
+ embed.set_image(url=image_url)
73
+
74
+ attachments_str = ", ".join([f"[{a.filename}]({a.url})" for a in message.attachments])
75
+ embed.add_field(name="Ekler (Dosyalar)", value=attachments_str[:1024], inline=False)
76
+
77
+ await self._send_log(message.guild, embed)
78
+
79
+ # 2. --- DÜZENLENEN MESAJ LOGLARI ---
80
+ @commands.Cog.listener()
81
+ async def on_message_edit(self, before: discord.Message, after: discord.Message):
82
+ if after.author.bot or not after.guild:
83
+ return
84
+ if not await Database.modul_aktif_mi(after.guild.id, "Logs"):
85
+ return
86
+ if before.content == after.content:
87
+ return
88
+
89
+ embed = discord.Embed(
90
+ title="📝 Mesaj Düzenlendi",
91
+ description=f"**Kanal:** {after.channel.mention}\n"
92
+ f"**Yazar:** {after.author.mention} (`{after.author.id}`)\n"
93
+ f"**Mesaja Git:** [Tıkla]({after.jump_url})",
94
+ color=CYBER_ORANGE,
95
+ timestamp=datetime.now()
96
+ )
97
+ embed.add_field(name="Eski İçerik", value=(before.content or "*Boş*")[:1024], inline=False)
98
+ embed.add_field(name="Yeni İçerik", value=(after.content or "*Boş*")[:1024], inline=False)
99
+
100
+ await self._send_log(after.guild, embed)
101
+
102
+ # 3. --- SES KANALI LOGLARI ---
103
+ @commands.Cog.listener()
104
+ async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState):
105
+ if member.bot or not member.guild:
106
+ return
107
+ if not await Database.modul_aktif_mi(member.guild.id, "Logs"):
108
+ return
109
+
110
+ embed = discord.Embed(
111
+ description=f"**Kullanıcı:** {member.mention} (`{member.id}`)",
112
+ timestamp=datetime.now()
113
+ )
114
+ embed.set_author(name=member.display_name, icon_url=member.display_avatar.url)
115
+
116
+ if before.channel is None and after.channel is not None:
117
+ embed.title = "🔊 Ses Kanalına Katıldı"
118
+ embed.description += f"\n**Katıldığı Kanal:** {after.channel.name} (`{after.channel.id}`)"
119
+ embed.color = CYBER_GREEN
120
+ await self._send_log(member.guild, embed)
121
+
122
+ elif before.channel is not None and after.channel is None:
123
+ embed.title = "🔇 Ses Kanalından Ayrıldı"
124
+ embed.description += f"\n**Ayrıldığı Kanal:** {before.channel.name} (`{before.channel.id}`)"
125
+ embed.color = CYBER_RED
126
+ await self._send_log(member.guild, embed)
127
+
128
+ elif before.channel is not None and after.channel is not None and before.channel.id != after.channel.id:
129
+ embed.title = "🔄 Ses Kanalı Değiştirdi"
130
+ embed.description += f"\n**Eski Kanal:** {before.channel.name}\n**Yeni Kanal:** {after.channel.name}"
131
+ embed.color = CYBER_BLUE
132
+ await self._send_log(member.guild, embed)
133
+
134
+ # 4. --- ÜYE GÜNCELLEME / NICK / ROL LOGLARI ---
135
+ @commands.Cog.listener()
136
+ async def on_member_update(self, before: discord.Member, after: discord.Member):
137
+ if before.bot or not after.guild:
138
+ return
139
+ if not await Database.modul_aktif_mi(after.guild.id, "Logs"):
140
+ return
141
+
142
+ embed = discord.Embed(
143
+ description=f"**Kullanıcı:** {after.mention} (`{after.id}`)",
144
+ timestamp=datetime.now()
145
+ )
146
+ embed.set_author(name=after.display_name, icon_url=after.display_avatar.url)
147
+
148
+ if before.roles != after.roles:
149
+ added_roles = [r for r in after.roles if r not in before.roles]
150
+ removed_roles = [r for r in before.roles if r not in after.roles]
151
+
152
+ if added_roles or removed_roles:
153
+ embed.title = "🛡️ Rolleri Güncellendi"
154
+ embed.color = CYBER_BLUE
155
+ if added_roles:
156
+ embed.add_field(name="Verilen Roller", value=", ".join([r.mention for r in added_roles]), inline=False)
157
+ if removed_roles:
158
+ embed.add_field(name="Alınan Roller", value=", ".join([r.mention for r in removed_roles]), inline=False)
159
+ await self._send_log(after.guild, embed)
160
+ return
161
+
162
+ if before.nick != after.nick:
163
+ embed.title = "👤 Sunucu Takma Adı Değiştirildi"
164
+ embed.color = CYBER_ORANGE
165
+ embed.add_field(name="Eski Ad", value=before.nick or before.name, inline=True)
166
+ embed.add_field(name="Yeni Ad", value=after.nick or after.name, inline=True)
167
+ await self._send_log(after.guild, embed)
168
+
169
+ # 5. --- KANAL OLUŞTURMA / SİLME LOGLARI ---
170
+ @commands.Cog.listener()
171
+ async def on_guild_channel_create(self, channel: discord.abc.GuildChannel):
172
+ if not await Database.modul_aktif_mi(channel.guild.id, "Logs"):
173
+ return
174
+ embed = discord.Embed(
175
+ title="🆕 Yeni Kanal Oluşturuldu",
176
+ description=f"**Kanal Adı:** {channel.name}\n**Tür:** `{str(channel.type)}`\n**ID:** `{channel.id}`",
177
+ color=CYBER_GREEN,
178
+ timestamp=datetime.now()
179
+ )
180
+ await self._send_log(channel.guild, embed)
181
+
182
+ @commands.Cog.listener()
183
+ async def on_guild_channel_delete(self, channel: discord.abc.GuildChannel):
184
+ if not await Database.modul_aktif_mi(channel.guild.id, "Logs"):
185
+ return
186
+ embed = discord.Embed(
187
+ title="🗑️ Kanal Silindi",
188
+ description=f"**Kanal Adı:** {channel.name}\n**Tür:** `{str(channel.type)}`\n**ID:** `{channel.id}`",
189
+ color=CYBER_RED,
190
+ timestamp=datetime.now()
191
+ )
192
+ await self._send_log(channel.guild, embed)
193
+
194
+ # 6. --- ROL OLUŞTURMA / SİLME LOGLARI ---
195
+ @commands.Cog.listener()
196
+ async def on_guild_role_create(self, role: discord.Role):
197
+ if not await Database.modul_aktif_mi(role.guild.id, "Logs"):
198
+ return
199
+ embed = discord.Embed(
200
+ title="🆕 Yeni Rol Oluşturuldu",
201
+ description=f"**Rol Adı:** {role.mention}\n**ID:** `{role.id}`",
202
+ color=CYBER_GREEN,
203
+ timestamp=datetime.now()
204
+ )
205
+ await self._send_log(role.guild, embed)
206
+
207
+ @commands.Cog.listener()
208
+ async def on_guild_role_delete(self, role: discord.Role):
209
+ if not await Database.modul_aktif_mi(role.guild.id, "Logs"):
210
+ return
211
+ embed = discord.Embed(
212
+ title="🗑️ Rol Silindi",
213
+ description=f"**Rol Adı:** {role.name}\n**ID:** `{role.id}`",
214
+ color=CYBER_RED,
215
+ timestamp=datetime.now()
216
+ )
217
+ await self._send_log(role.guild, embed)
218
+
219
+ # 7. --- YASAKLAMA / YASAK KALDIRMA LOGLARI ---
220
+ @commands.Cog.listener()
221
+ async def on_member_ban(self, guild: discord.Guild, user: discord.User):
222
+ if not await Database.modul_aktif_mi(guild.id, "Logs"):
223
+ return
224
+ embed = discord.Embed(
225
+ title="🔨 Üye Yasaklandı (Ban)",
226
+ description=f"**Kullanıcı:** {user.mention} (`{user.id}`)\n**Hesap Açılış Tarihi:** {user.created_at.strftime('%d.%m.%Y')}",
227
+ color=CYBER_RED,
228
+ timestamp=datetime.now()
229
+ )
230
+ embed.set_thumbnail(url=user.display_avatar.url)
231
+ await self._send_log(guild, embed)
232
+
233
+ @commands.Cog.listener()
234
+ async def on_member_unban(self, guild: discord.Guild, user: discord.User):
235
+ if not await Database.modul_aktif_mi(guild.id, "Logs"):
236
+ return
237
+ embed = discord.Embed(
238
+ title="🔓 Üye Yasağı Kaldırıldı (Unban)",
239
+ description=f"**Kullanıcı:** {user.mention} (`{user.id}`)",
240
+ color=CYBER_GREEN,
241
+ timestamp=datetime.now()
242
+ )
243
+ embed.set_thumbnail(url=user.display_avatar.url)
244
+ await self._send_log(guild, embed)
245
+
246
+ # 8. --- ÜYE KATILIM & AYRILMA LOGLARI ---
247
+ @commands.Cog.listener()
248
+ async def on_member_join(self, member: discord.Member):
249
+ if not await Database.modul_aktif_mi(member.guild.id, "Logs"):
250
+ return
251
+ embed = discord.Embed(
252
+ title="📥 Üye Sunucuya Katıldı",
253
+ description=f"**Kullanıcı:** {member.mention} (`{member.id}`)\n"
254
+ f"**Hesap Açılış Tarihi:** {member.created_at.strftime('%d.%m.%Y %H:%M:%S')}\n"
255
+ f"**Toplam Üye:** `{member.guild.member_count}`",
256
+ color=CYBER_GREEN,
257
+ timestamp=datetime.now()
258
+ )
259
+ embed.set_thumbnail(url=member.display_avatar.url)
260
+ await self._send_log(member.guild, embed)
261
+
262
+ @commands.Cog.listener()
263
+ async def on_member_remove(self, member: discord.Member):
264
+ if not await Database.modul_aktif_mi(member.guild.id, "Logs"):
265
+ return
266
+ embed = discord.Embed(
267
+ title="📤 Üye Sunucudan Ayrıldı",
268
+ description=f"**Kullanıcı:** {member.mention} (`{member.id}`)\n"
269
+ f"**Toplam Üye:** `{member.guild.member_count}`",
270
+ color=CYBER_RED,
271
+ timestamp=datetime.now()
272
+ )
273
+ embed.set_thumbnail(url=member.display_avatar.url)
274
+ await self._send_log(member.guild, embed)
275
+
276
+ # 9. --- DAVET OLUŞTURMA & SİLME LOGLARI ---
277
+ @commands.Cog.listener()
278
+ async def on_invite_create(self, invite: discord.Invite):
279
+ if not await Database.modul_aktif_mi(invite.guild.id, "Logs"):
280
+ return
281
+ embed = discord.Embed(
282
+ title="🎫 Davet Bağlantısı Oluşturuldu",
283
+ description=f"**Oluşturan:** {invite.inviter.mention if invite.inviter else 'Bilinmiyor'} (`{invite.inviter.id if invite.inviter else ''}`)\n"
284
+ f"**Kod:** `{invite.code}`\n"
285
+ f"**Kanal:** {invite.channel.mention if invite.channel else 'Belirsiz'}\n"
286
+ f"**Maksimum Kullanım:** `{invite.max_uses if invite.max_uses else 'Sınırsız'}`\n"
287
+ f"**Süre:** `{invite.max_age if invite.max_age else 'Süresiz'}` saniye",
288
+ color=CYBER_GREEN,
289
+ timestamp=datetime.now()
290
+ )
291
+ await self._send_log(invite.guild, embed)
292
+
293
+ @commands.Cog.listener()
294
+ async def on_invite_delete(self, invite: discord.Invite):
295
+ if not await Database.modul_aktif_mi(invite.guild.id, "Logs"):
296
+ return
297
+ embed = discord.Embed(
298
+ title="🗑️ Davet Bağlantısı Silindi",
299
+ description=f"**Kod:** `{invite.code}`\n**Kanal:** {invite.channel.mention if invite.channel else 'Belirsiz'}",
300
+ color=CYBER_RED,
301
+ timestamp=datetime.now()
302
+ )
303
+ await self._send_log(invite.guild, embed)
304
+
305
+ # 10. --- SUNUCU (GUILD) GÜNCELLEME LOGLARI ---
306
+ @commands.Cog.listener()
307
+ async def on_guild_update(self, before: discord.Guild, after: discord.Guild):
308
+ if not await Database.modul_aktif_mi(after.id, "Logs"):
309
+ return
310
+ embed = discord.Embed(
311
+ title="⚙️ Sunucu Ayarları Güncellendi",
312
+ color=CYBER_ORANGE,
313
+ timestamp=datetime.now()
314
+ )
315
+
316
+ changes = []
317
+ if before.name != after.name:
318
+ changes.append(f"**Sunucu Adı Değişti:** `{before.name}` ➡️ `{after.name}`")
319
+ if before.afk_channel != after.afk_channel:
320
+ changes.append(f"**AFK Kanalı Değişti:** `{before.afk_channel.name if before.afk_channel else 'Yok'}` ➡️ `{after.afk_channel.name if after.afk_channel else 'Yok'}`")
321
+ if before.verification_level != after.verification_level:
322
+ changes.append(f"**Doğrulama Seviyesi:** `{before.verification_level}` ➡️ `{after.verification_level}`")
323
+ if before.premium_tier != after.premium_tier:
324
+ changes.append(f"**Takviye Seviyesi:** `Seviye {before.premium_tier}` ➡️ `Seviye {after.premium_tier}` (Toplam Takviye: `{after.premium_subscription_count}`)")
325
+
326
+ if not changes:
327
+ return
328
+
329
+ embed.description = "\n".join(changes)
330
+ await self._send_log(after, embed)
331
+
332
+ # 11. --- ROL GÜNCELLEME LOGLARI ---
333
+ @commands.Cog.listener()
334
+ async def on_guild_role_update(self, before: discord.Role, after: discord.Role):
335
+ if not await Database.modul_aktif_mi(after.guild.id, "Logs"):
336
+ return
337
+ embed = discord.Embed(
338
+ title="🛡️ Rol Ayarları Güncellendi",
339
+ description=f"**Rol:** {after.mention} (`{after.id}`)",
340
+ color=CYBER_ORANGE,
341
+ timestamp=datetime.now()
342
+ )
343
+
344
+ changes = []
345
+ if before.name != after.name:
346
+ changes.append(f"**Adı Değişti:** `{before.name}` ➡️ `{after.name}`")
347
+ if before.color != after.color:
348
+ changes.append(f"**Rengi Değişti:** `{before.color}` ➡️ `{after.color}`")
349
+ if before.permissions != after.permissions:
350
+ changes.append(f"**Yetkileri Değiştirildi.** (Farklılıklar tespit edildi)")
351
+
352
+ if not changes:
353
+ return
354
+
355
+ embed.description += "\n\n" + "\n".join(changes)
356
+ await self._send_log(after.guild, embed)
357
+
358
+ # 12. --- KANAL AYARI GÜNCELLEME LOGLARI ---
359
+ @commands.Cog.listener()
360
+ async def on_guild_channel_update(self, before: discord.abc.GuildChannel, after: discord.abc.GuildChannel):
361
+ if not await Database.modul_aktif_mi(after.guild.id, "Logs"):
362
+ return
363
+ embed = discord.Embed(
364
+ title="⚙️ Kanal Ayarları Güncellendi",
365
+ description=f"**Kanal:** {after.mention if hasattr(after, 'mention') else after.name} (`{after.id}`)",
366
+ color=CYBER_ORANGE,
367
+ timestamp=datetime.now()
368
+ )
369
+
370
+ changes = []
371
+ if before.name != after.name:
372
+ changes.append(f"**Adı Değişti:** `{before.name}` ➡️ `{after.name}`")
373
+ if hasattr(before, 'topic') and hasattr(after, 'topic') and before.topic != after.topic:
374
+ changes.append(f"**Konusu Değişti:**\n*Eski:* {before.topic or 'Yok'}\n*Yeni:* {after.topic or 'Yok'}")
375
+ if hasattr(before, 'slowmode_delay') and hasattr(after, 'slowmode_delay') and before.slowmode_delay != after.slowmode_delay:
376
+ changes.append(f"**Yavaş Mod Değişti:** `{before.slowmode_delay}s` ➡️ `{after.slowmode_delay}s`")
377
+
378
+ if not changes:
379
+ return
380
+
381
+ embed.description += "\n\n" + "\n".join(changes)
382
+ await self._send_log(after.guild, embed)
383
+
384
+ # 13. --- EMOJI & STICKER GÜNCELLEME LOGLARI ---
385
+ @commands.Cog.listener()
386
+ async def on_guild_emojis_update(self, guild: discord.Guild, before: list[discord.Emoji], after: list[discord.Emoji]):
387
+ if not await Database.modul_aktif_mi(guild.id, "Logs"):
388
+ return
389
+ embed = discord.Embed(
390
+ title="🎨 Emojiler Güncellendi",
391
+ color=CYBER_ORANGE,
392
+ timestamp=datetime.now()
393
+ )
394
+
395
+ if len(before) < len(after):
396
+ added = [e for e in after if e not in before][0]
397
+ embed.description = f"➕ **Yeni Emoji Eklendi:** {added} (`{added.name}` | ID: `{added.id}`)"
398
+ embed.color = CYBER_GREEN
399
+ elif len(before) > len(after):
400
+ removed = [e for e in before if e not in after][0]
401
+ embed.description = f"➖ **Emoji Silindi:** `{removed.name}` (ID: `{removed.id}`)"
402
+ embed.color = CYBER_RED
403
+ else:
404
+ # Emoji ismi değişmiş olabilir
405
+ for e_before in before:
406
+ for e_after in after:
407
+ if e_before.id == e_after.id and e_before.name != e_after.name:
408
+ embed.description = f"✏️ **Emoji Adı Değiştirildi:** `{e_before.name}` ➡️ `{e_after.name}` {e_after}"
409
+ break
410
+ if not embed.description:
411
+ return
412
+
413
+ await self._send_log(guild, embed)
414
+
415
+ @commands.Cog.listener()
416
+ async def on_guild_stickers_update(self, guild: discord.Guild, before: list[discord.GuildSticker], after: list[discord.GuildSticker]):
417
+ if not await Database.modul_aktif_mi(guild.id, "Logs"):
418
+ return
419
+ embed = discord.Embed(
420
+ title="🖼️ Çıkartmalar (Stickers) Güncellendi",
421
+ color=CYBER_ORANGE,
422
+ timestamp=datetime.now()
423
+ )
424
+
425
+ if len(before) < len(after):
426
+ added = [s for s in after if s not in before][0]
427
+ embed.description = f"➕ **Yeni Çıkartma Eklendi:** `{added.name}` (ID: `{added.id}`)"
428
+ embed.color = CYBER_GREEN
429
+ elif len(before) > len(after):
430
+ removed = [s for s in before if s not in after][0]
431
+ embed.description = f"➖ **Çıkartma Silindi:** `{removed.name}` (ID: `{removed.id}`)"
432
+ embed.color = CYBER_RED
433
+ else:
434
+ for s_before in before:
435
+ for s_after in after:
436
+ if s_before.id == s_after.id and s_before.name != s_after.name:
437
+ embed.description = f"✏️ **Çıkartma Adı Değiştirildi:** `{s_before.name}` ➡️ `{s_after.name}`"
438
+ break
439
+ if not embed.description:
440
+ return
441
+
442
+ await self._send_log(guild, embed)
443
+
444
+ from database import Database
445
+ async def setup(bot):
446
+ await bot.add_cog(Logs(bot))
cogs/moderation.py ADDED
@@ -0,0 +1,558 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ import re
5
+ from datetime import datetime, timedelta
6
+ from database import Database
7
+
8
+ # Premium HSL Renk Paleti (Embed'ler için)
9
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
10
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
11
+ CYBER_ORANGE = discord.Color.from_rgb(230, 126, 34)
12
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
13
+ CYBER_DARK = discord.Color.from_rgb(44, 62, 80)
14
+
15
+ def parse_duration(duration_str: str) -> timedelta:
16
+ """Süre dizesini ayrıştırır (örn. '10m', '2h', '1d', '30s')"""
17
+ match = re.match(r"^(\d+)([smhd])$", duration_str.lower().strip())
18
+ if not match:
19
+ raise ValueError("Geçersiz süre biçimi. Örnek: 10m, 2h, 1d, 30s")
20
+ value, unit = int(match.group(1)), match.group(2)
21
+ if unit == 's':
22
+ return timedelta(seconds=value)
23
+ elif unit == 'm':
24
+ return timedelta(minutes=value)
25
+ elif unit == 'h':
26
+ return timedelta(hours=value)
27
+ elif unit == 'd':
28
+ return timedelta(days=value)
29
+ return timedelta(0)
30
+
31
+ async def safe_send(ctx, *args, **kwargs):
32
+ """Slash ve prefix komutlarında güvenli mesaj gönderir."""
33
+ try:
34
+ if ctx.interaction and not ctx.interaction.response.is_done():
35
+ return await ctx.send(*args, **kwargs)
36
+ elif ctx.interaction and ctx.interaction.response.is_done():
37
+ return await ctx.interaction.followup.send(*args, **kwargs)
38
+ else:
39
+ return await ctx.send(*args, **kwargs)
40
+ except Exception:
41
+ try:
42
+ return await ctx.send(*args, **kwargs)
43
+ except Exception:
44
+ pass
45
+
46
+ async def send_dm_notification(member: discord.Member, action_name: str, guild_name: str, reason: str, duration: str = None, warn_count: int = None):
47
+ """Kullanıcıya ceza veya işlem ile ilgili DM kutusuna şık bir bildirim gönderir."""
48
+ colors = {
49
+ "Yasaklama (Ban)": discord.Color.from_rgb(231, 76, 60),
50
+ "Sunucudan Atılma (Kick)": discord.Color.from_rgb(230, 126, 34),
51
+ "Susturulma (Mute)": discord.Color.from_rgb(230, 126, 34),
52
+ "Susturma Kaldırıldı": discord.Color.from_rgb(46, 204, 113),
53
+ "Uyarı": discord.Color.from_rgb(241, 196, 15),
54
+ }
55
+
56
+ color = colors.get(action_name, discord.Color.from_rgb(52, 152, 219))
57
+
58
+ embed = discord.Embed(
59
+ title=f"⚖️ {guild_name} Sunucusunda İşlem Yapıldı",
60
+ description=f"Merhaba **{member.name}**, sunucumuzda kurallara uyumu sağlamak amacıyla hesabınıza yönelik bir işlem uygulanmıştır.",
61
+ color=color,
62
+ timestamp=datetime.now()
63
+ )
64
+
65
+ embed.add_field(name="📌 Uygulanan İşlem", value=f"**{action_name}**", inline=True)
66
+ if duration:
67
+ embed.add_field(name="⏳ Süre", value=f"`{duration}`", inline=True)
68
+ if warn_count is not None:
69
+ embed.add_field(name="⚠️ Toplam Uyarı Sayısı", value=f"`{warn_count}`", inline=True)
70
+
71
+ embed.add_field(name="📝 Sebep", value=f"{reason}", inline=False)
72
+ embed.set_footer(text="ArazAI Siber Asayiş ve Güvenlik Sistemi")
73
+
74
+ try:
75
+ await member.send(embed=embed)
76
+ return True
77
+ except Exception:
78
+ return False
79
+
80
+ class Moderation(commands.Cog):
81
+ def __init__(self, bot):
82
+ self.bot = bot
83
+
84
+ async def cog_load(self):
85
+ await Database.execute("""
86
+ CREATE TABLE IF NOT EXISTS ip_bans (
87
+ guild_id INTEGER,
88
+ user_id INTEGER,
89
+ user_name TEXT,
90
+ moderator_id INTEGER,
91
+ reason TEXT,
92
+ timestamp TEXT,
93
+ PRIMARY KEY (guild_id, user_id)
94
+ )
95
+ """)
96
+
97
+ @commands.hybrid_command(name="ip-ban", description="Belirtilen üyeyi IP adresi dahil sunucudan tamamen yasaklar.")
98
+ @commands.has_permissions(ban_members=True)
99
+ @app_commands.describe(member="IP Yasaklanacak üye", reason="Yasaklanma sebebi")
100
+ async def ipban(self, ctx: commands.Context, member: discord.Member, *, reason: str = "Belirtilmedi"):
101
+ if ctx.interaction:
102
+ await ctx.defer()
103
+ if member.top_role >= ctx.author.top_role and ctx.author.id != ctx.guild.owner_id:
104
+ await safe_send(ctx, "❌ Sizinle aynı veya daha yüksek roldeki birini yasaklayamazsınız!", ephemeral=True)
105
+ return
106
+ if member.top_role >= ctx.guild.me.top_role:
107
+ await safe_send(ctx, "❌ Bu üyeyi yasaklamak için yetkim yetersiz.", ephemeral=True)
108
+ return
109
+
110
+ try:
111
+ await send_dm_notification(member, "Yasaklama (Ban)", ctx.guild.name, f"[IP BAN] {reason}")
112
+ # Discord bans natively ban by IP
113
+ await member.ban(delete_message_days=7, reason=f"IP BAN | Yetkili: {ctx.author} | Sebep: {reason}")
114
+
115
+ # Save to Database
116
+ now_str = datetime.now().isoformat()
117
+ await Database.execute(
118
+ "INSERT OR REPLACE INTO ip_bans (guild_id, user_id, user_name, moderator_id, reason, timestamp) VALUES (?, ?, ?, ?, ?, ?)",
119
+ (ctx.guild.id, member.id, str(member), ctx.author.id, reason, now_str)
120
+ )
121
+
122
+ embed = discord.Embed(
123
+ title="🛡️ IP Yasaklaması Uygulandı",
124
+ description=f"**Kullanıcı:** {member.mention} ({member.id})\n**Yetkili:** {ctx.author.mention}\n**Sebep:** {reason}\n*Not: Kullanıcının hesabı ve IP adresi Discord sunucusundan tamamen engellenmiştir.*",
125
+ color=CYBER_RED,
126
+ timestamp=datetime.now()
127
+ )
128
+ embed.set_thumbnail(url=member.display_avatar.url)
129
+ await safe_send(ctx, embed=embed)
130
+ except Exception as e:
131
+ await safe_send(ctx, f"❌ Üye IP yasaklanırken bir hata oluştu: {e}", ephemeral=True)
132
+
133
+ @commands.hybrid_command(name="ban", description="Belirtilen üyeyi sunucudan yasaklar.")
134
+ @commands.has_permissions(ban_members=True)
135
+ @app_commands.describe(member="Yasaklanacak üye", reason="Yasaklanma sebebi")
136
+ async def ban(self, ctx: commands.Context, member: discord.Member, *, reason: str = "Belirtilmedi"):
137
+ if ctx.interaction:
138
+ await ctx.defer()
139
+ if member.top_role >= ctx.author.top_role and ctx.author.id != ctx.guild.owner_id:
140
+ await safe_send(ctx, "❌ Sizinle aynı veya daha yüksek roldeki birini yasaklayamazsınız!", ephemeral=True)
141
+ return
142
+ if member.top_role >= ctx.guild.me.top_role:
143
+ await safe_send(ctx, "❌ Bu üyeyi yasaklamak için yetkim yetersiz (Rolüm üyenin rolünden düşük).", ephemeral=True)
144
+ return
145
+
146
+ try:
147
+ await send_dm_notification(member, "Yasaklama (Ban)", ctx.guild.name, reason)
148
+ await member.ban(reason=f"Yetkili: {ctx.author} | Sebep: {reason}")
149
+ embed = discord.Embed(
150
+ title="🔨 Üye Yasaklandı",
151
+ description=f"**Kullanıcı:** {member.mention} ({member.id})\n**Yetkili:** {ctx.author.mention}\n**Sebep:** {reason}",
152
+ color=CYBER_RED,
153
+ timestamp=datetime.now()
154
+ )
155
+ embed.set_thumbnail(url=member.display_avatar.url)
156
+ await safe_send(ctx, embed=embed)
157
+ except Exception as e:
158
+ await safe_send(ctx, f"❌ Üye yasaklanırken bir hata oluştu: {e}", ephemeral=True)
159
+
160
+ @commands.hybrid_command(name="kick", description="Belirtilen üyeyi sunucudan atar.")
161
+ @commands.has_permissions(kick_members=True)
162
+ @app_commands.describe(member="Atılacak üye", reason="Atılma sebebi")
163
+ async def kick(self, ctx: commands.Context, member: discord.Member, *, reason: str = "Belirtilmedi"):
164
+ if ctx.interaction:
165
+ await ctx.defer()
166
+ if member.top_role >= ctx.author.top_role and ctx.author.id != ctx.guild.owner_id:
167
+ await safe_send(ctx, "❌ Sizinle aynı veya daha yüksek roldeki birini sunucudan atamazsınız!", ephemeral=True)
168
+ return
169
+ if member.top_role >= ctx.guild.me.top_role:
170
+ await safe_send(ctx, "❌ Bu üyeyi atmak için yetkim yetersiz.", ephemeral=True)
171
+ return
172
+
173
+ try:
174
+ await send_dm_notification(member, "Sunucudan Atılma (Kick)", ctx.guild.name, reason)
175
+ await member.kick(reason=f"Yetkili: {ctx.author} | Sebep: {reason}")
176
+ embed = discord.Embed(
177
+ title="👢 Üye Atıldı",
178
+ description=f"**Kullanıcı:** {member.mention} ({member.id})\n**Yetkili:** {ctx.author.mention}\n**Sebep:** {reason}",
179
+ color=CYBER_ORANGE,
180
+ timestamp=datetime.now()
181
+ )
182
+ embed.set_thumbnail(url=member.display_avatar.url)
183
+ await safe_send(ctx, embed=embed)
184
+ except Exception as e:
185
+ await safe_send(ctx, f"❌ Üye atılırken bir hata oluştu: {e}", ephemeral=True)
186
+
187
+ @commands.hybrid_command(name="mute", description="Belirtilen üyeyi susturur (Timeout).")
188
+ @commands.has_permissions(moderate_members=True)
189
+ @app_commands.describe(member="Susturulacak üye", duration="Süre (örn: 10m, 1h, 1d)", reason="Susturulma sebebi")
190
+ async def mute(self, ctx: commands.Context, member: discord.Member, duration: str, *, reason: str = "Belirtilmedi"):
191
+ if ctx.interaction:
192
+ await ctx.defer()
193
+ if member.top_role >= ctx.author.top_role and ctx.author.id != ctx.guild.owner_id:
194
+ await safe_send(ctx, "❌ Sizinle aynı veya daha yüksek roldeki birini susturamazsınız!", ephemeral=True)
195
+ return
196
+ if member.top_role >= ctx.guild.me.top_role:
197
+ await safe_send(ctx, "❌ Bu üyeyi susturmak için yetkim yetersiz.", ephemeral=True)
198
+ return
199
+
200
+ try:
201
+ delta = parse_duration(duration)
202
+ if delta.total_seconds() <= 0:
203
+ await safe_send(ctx, "❌ Geçersiz süre girdiniz.", ephemeral=True)
204
+ return
205
+
206
+ # Discord'un timeout API'si UTC datetime kabul eder
207
+ unmute_time_utc = discord.utils.utcnow() + delta
208
+ unmute_time_local = datetime.now() + delta
209
+
210
+ await send_dm_notification(member, "Susturulma (Mute)", ctx.guild.name, reason, duration=duration)
211
+ await member.timeout(unmute_time_utc, reason=f"Yetkili: {ctx.author} | Sebep: {reason}")
212
+ await Database.mute_ekle(member.id, ctx.guild.id, unmute_time_local)
213
+
214
+ embed = discord.Embed(
215
+ title="🔇 Üye Susturuldu",
216
+ description=f"**Kullanıcı:** {member.mention}\n**Yetkili:** {ctx.author.mention}\n**Süre:** {duration}\n**Açılma Tarihi:** <t:{int(unmute_time_local.timestamp())}:F>\n**Sebep:** {reason}",
217
+ color=CYBER_ORANGE,
218
+ timestamp=datetime.now()
219
+ )
220
+ embed.set_thumbnail(url=member.display_avatar.url)
221
+ await safe_send(ctx, embed=embed)
222
+ except ValueError as ve:
223
+ await safe_send(ctx, f"❌ Hata: {ve}", ephemeral=True)
224
+ except Exception as e:
225
+ await safe_send(ctx, f"❌ Susturma işlemi başarısız oldu: {e}", ephemeral=True)
226
+
227
+ @commands.hybrid_command(name="unmute", description="Belirtilen üyenin susturmasını kaldırır.")
228
+ @commands.has_permissions(moderate_members=True)
229
+ @app_commands.describe(member="Susturması kaldırılacak üye")
230
+ async def unmute(self, ctx: commands.Context, member: discord.Member):
231
+ if ctx.interaction:
232
+ await ctx.defer()
233
+ try:
234
+ await member.timeout(None, reason=f"Susturma kaldırıldı: {ctx.author}")
235
+ await Database.mute_kaldir(member.id, ctx.guild.id)
236
+ await send_dm_notification(member, "Susturma Kaldırıldı", ctx.guild.name, f"Yetkili: {ctx.author}")
237
+
238
+ embed = discord.Embed(
239
+ title="🔊 Susturma Kaldırıldı",
240
+ description=f"**Kullanıcı:** {member.mention}\n**Yetkili:** {ctx.author.mention}",
241
+ color=CYBER_GREEN,
242
+ timestamp=datetime.now()
243
+ )
244
+ embed.set_thumbnail(url=member.display_avatar.url)
245
+ await safe_send(ctx, embed=embed)
246
+ except Exception as e:
247
+ await safe_send(ctx, f"❌ Susturma kaldırılamadı: {e}", ephemeral=True)
248
+
249
+ @commands.hybrid_command(name="warn", description="Belirtilen üyeye uyarı ekler.")
250
+ @commands.has_permissions(manage_messages=True)
251
+ @app_commands.describe(member="Uyarılacak üye", reason="Uyarı sebebi")
252
+ async def warn(self, ctx: commands.Context, member: discord.Member, *, reason: str):
253
+ if ctx.interaction:
254
+ await ctx.defer()
255
+ if member.top_role >= ctx.author.top_role and ctx.author.id != ctx.guild.owner_id:
256
+ await safe_send(ctx, "❌ Sizinle aynı veya daha yüksek roldeki birini uyaramazsınız!", ephemeral=True)
257
+ return
258
+
259
+ try:
260
+ warn_id = await Database.uyari_ekle(member.id, ctx.guild.id, ctx.author.id, reason)
261
+ warns = await Database.uyarilari_getir(member.id, ctx.guild.id)
262
+ warn_count = len(warns)
263
+
264
+ await send_dm_notification(member, "Uyarı", ctx.guild.name, reason, warn_count=warn_count)
265
+
266
+ embed = discord.Embed(
267
+ title="⚠️ Üye Uyarıldı",
268
+ description=f"**Kullanıcı:** {member.mention}\n**Yetkili:** {ctx.author.mention}\n**Uyarı ID:** `{warn_id}`\n**Sebep:** {reason}\n**Toplam Uyarı Sayısı:** `{warn_count}`",
269
+ color=CYBER_ORANGE,
270
+ timestamp=datetime.now()
271
+ )
272
+ embed.set_thumbnail(url=member.display_avatar.url)
273
+ await safe_send(ctx, embed=embed)
274
+
275
+ # Otomatik cezalar
276
+ if warn_count >= 5:
277
+ await ctx.channel.send(f"⚠️ {member.mention} 5 uyarı sınırına ulaştığı için sunucudan **atılıyor**.")
278
+ await send_dm_notification(member, "Sunucudan Atılma (Kick)", ctx.guild.name, "5 Uyarı sınırına ulaşıldı (Otomatik).")
279
+ await member.kick(reason="5 Uyarı sınırına ulaşıldı.")
280
+ elif warn_count >= 3:
281
+ await ctx.channel.send(f"⚠️ {member.mention} 3 uyarı sınırına ulaştığı için **1 saat** susturuluyor.")
282
+ delta = timedelta(hours=1)
283
+ unmute_time_utc = discord.utils.utcnow() + delta
284
+ await send_dm_notification(member, "Susturulma (Mute)", ctx.guild.name, "3 Uyarı sınırına ulaşıldı (Otomatik 1 Saat Mute).", duration="1 Saat")
285
+ await member.timeout(unmute_time_utc, reason="3 Uyarı sınırına ulaşıldı (Otomatik 1 Saat Mute).")
286
+ await Database.mute_ekle(member.id, ctx.guild.id, datetime.now() + delta)
287
+
288
+ except Exception as e:
289
+ await safe_send(ctx, f"❌ Uyarı eklenemedi: {e}", ephemeral=True)
290
+
291
+ @commands.hybrid_command(name="warns", description="Belirtilen üyenin tüm uyarılarını listeler.")
292
+ @commands.has_permissions(manage_messages=True)
293
+ @app_commands.describe(member="Uyarıları görüntülenecek üye")
294
+ async def warns(self, ctx: commands.Context, member: discord.Member):
295
+ if ctx.interaction:
296
+ await ctx.defer()
297
+ try:
298
+ records = await Database.uyarilari_getir(member.id, ctx.guild.id)
299
+ if not records:
300
+ await safe_send(ctx, f"ℹ️ {member.mention} kullanıcısının geçmiş uyarısı bulunmamaktadır.")
301
+ return
302
+
303
+ embed = discord.Embed(
304
+ title=f"📋 Uyarı Geçmişi - {member.display_name}",
305
+ description=f"{member.mention} kullanıcısının toplam `{len(records)}` uyarısı var.",
306
+ color=CYBER_BLUE,
307
+ timestamp=datetime.now()
308
+ )
309
+ embed.set_thumbnail(url=member.display_avatar.url)
310
+
311
+ for record in records[:10]:
312
+ moderator = ctx.guild.get_member(record['moderator_id'])
313
+ mod_name = moderator.mention if moderator else f"ID: {record['moderator_id']}"
314
+ tarih_dt = datetime.fromisoformat(record['tarih'])
315
+ tarih_stamp = f"<t:{int(tarih_dt.timestamp())}:f>"
316
+ embed.add_field(
317
+ name=f"Uyarı ID: #{record['id']}",
318
+ value=f"**Yetkili:** {mod_name}\n**Tarih:** {tarih_stamp}\n**Sebep:** {record['sebep']}",
319
+ inline=False
320
+ )
321
+
322
+ await safe_send(ctx, embed=embed)
323
+ except Exception as e:
324
+ await safe_send(ctx, f"❌ Uyarılar listelenemedi: {e}", ephemeral=True)
325
+
326
+ @commands.hybrid_command(name="warn-sil", description="ID bazlı olarak tek bir uyarıyı siler.")
327
+ @commands.has_permissions(manage_messages=True)
328
+ @app_commands.describe(warn_id="Silinecek uyarının sayısal benzersiz ID'si")
329
+ async def warn_sil(self, ctx: commands.Context, warn_id: int):
330
+ if ctx.interaction:
331
+ await ctx.defer()
332
+ try:
333
+ rows_deleted = await Database.uyari_sil(warn_id, ctx.guild.id)
334
+ if rows_deleted > 0:
335
+ await safe_send(ctx, f"✅ #{warn_id} ID'li uyarı başarıyla silindi.")
336
+ else:
337
+ await safe_send(ctx, "❌ Belirtilen ID'ye sahip bir uyarı bu sunucuda bulunamadı.", ephemeral=True)
338
+ except Exception as e:
339
+ await safe_send(ctx, f"❌ Uyarı silinirken hata: {e}", ephemeral=True)
340
+
341
+ @commands.hybrid_command(name="warn-temizle", description="Bir üyenin tüm uyarılarını temizler.")
342
+ @commands.has_permissions(administrator=True)
343
+ @app_commands.describe(member="Uyarıları sıfırlanacak üye")
344
+ async def warn_temizle(self, ctx: commands.Context, member: discord.Member):
345
+ if ctx.interaction:
346
+ await ctx.defer()
347
+ try:
348
+ rows_deleted = await Database.uyarilari_temizle(member.id, ctx.guild.id)
349
+ await safe_send(ctx, f"✅ {member.mention} kullanıcısının tüm uyarıları sıfırlandı. Toplam `{rows_deleted}` uyarı silindi.")
350
+ except Exception as e:
351
+ await safe_send(ctx, f"❌ Uyarılar silinirken hata: {e}", ephemeral=True)
352
+
353
+ # --- EKSTRA GÜVENLİK VE MODERASYON KOMUTLARI ---
354
+
355
+ @commands.hybrid_command(name="unban", description="Belirtilen kullanıcının yasaklamasını kaldırır.")
356
+ @commands.has_permissions(ban_members=True)
357
+ @app_commands.describe(user_id="Yasağı kaldırılacak kullanıcının ID'si (Sayısal)")
358
+ async def unban(self, ctx: commands.Context, user_id: str):
359
+ if ctx.interaction:
360
+ await ctx.defer()
361
+ try:
362
+ uid = int(user_id)
363
+ user = await self.bot.fetch_user(uid)
364
+ if not user:
365
+ await safe_send(ctx, "❌ Belirtilen ID'ye sahip bir Discord kullanıcısı bulunamadı.", ephemeral=True)
366
+ return
367
+
368
+ await ctx.guild.unban(user, reason=f"Yasağı kaldıran yetkili: {ctx.author}")
369
+ embed = discord.Embed(
370
+ title="🔓 Yasak Kaldırıldı",
371
+ description=f"**Kullanıcı:** {user.name} ({user.id})\n**Yetkili:** {ctx.author.mention}",
372
+ color=CYBER_GREEN,
373
+ timestamp=datetime.now()
374
+ )
375
+ await safe_send(ctx, embed=embed)
376
+ except ValueError:
377
+ await safe_send(ctx, "❌ Geçersiz kullanıcı ID'si. Sadece rakamlardan oluşmalıdır.", ephemeral=True)
378
+ except discord.NotFound:
379
+ await safe_send(ctx, "❌ Bu kullanıcı zaten sunucuda yasaklı değil.", ephemeral=True)
380
+ except Exception as e:
381
+ await safe_send(ctx, f"❌ İşlem başarısız: {e}", ephemeral=True)
382
+
383
+ @commands.hybrid_command(name="clear", description="Belirtilen miktarda mesajı kanaldan temizler.")
384
+ @commands.has_permissions(manage_messages=True)
385
+ @app_commands.describe(amount="Temizlenecek mesaj sayısı (1-100)")
386
+ async def clear(self, ctx: commands.Context, amount: int):
387
+ if amount < 1 or amount > 100:
388
+ await safe_send(ctx, "❌ Lütfen 1 ile 100 arasında bir sayı giriniz.", ephemeral=True)
389
+ return
390
+
391
+ try:
392
+ if ctx.interaction:
393
+ await ctx.defer(ephemeral=True)
394
+
395
+ deleted = await ctx.channel.purge(limit=amount)
396
+
397
+ if ctx.interaction:
398
+ await ctx.interaction.followup.send(content=f"✅ Başarıyla `{len(deleted)}` mesaj temizlendi.", ephemeral=True)
399
+ else:
400
+ await ctx.send(f"✅ Başarıyla `{len(deleted)}` mesaj temizlendi.", delete_after=5)
401
+ except Exception as e:
402
+ await safe_send(ctx, f"❌ Temizleme işlemi başarısız oldu: {e}", ephemeral=True)
403
+
404
+ @commands.hybrid_command(name="slowmode", description="Kanal için yavaş mod süresini ayarlar.")
405
+ @commands.has_permissions(manage_channels=True)
406
+ @app_commands.describe(seconds="Saniye cinsinden yavaş mod süresi (0 kapatır)")
407
+ async def slowmode(self, ctx: commands.Context, seconds: int):
408
+ if ctx.interaction:
409
+ await ctx.defer()
410
+ if seconds < 0 or seconds > 21600:
411
+ await safe_send(ctx, "❌ Yavaş mod süresi 0 ile 21600 saniye (6 saat) arasında olmalıdır.", ephemeral=True)
412
+ return
413
+
414
+ try:
415
+ await ctx.channel.edit(slowmode_delay=seconds)
416
+ if seconds == 0:
417
+ await safe_send(ctx, "✅ Yavaş mod bu kanal için kapatıldı.")
418
+ else:
419
+ await safe_send(ctx, f"✅ Yavaş mod bu kanal için `{seconds}` saniye olarak ayarlandı.")
420
+ except Exception as e:
421
+ await safe_send(ctx, f"❌ İşlem başarısız: {e}", ephemeral=True)
422
+
423
+ @commands.hybrid_command(name="lock", description="Kanalı @everyone rolüne karşı mesaj gönderimine kapatır.")
424
+ @commands.has_permissions(manage_channels=True)
425
+ async def lock(self, ctx: commands.Context):
426
+ if ctx.interaction:
427
+ await ctx.defer()
428
+ try:
429
+ overwrite = ctx.channel.overwrites_for(ctx.guild.default_role)
430
+ overwrite.send_messages = False
431
+ await ctx.channel.set_permissions(ctx.guild.default_role, overwrite=overwrite, reason=f"Kanal kilitlendi: {ctx.author}")
432
+ await safe_send(ctx, "🔒 Kanal başarıyla yazıma **kilitlendi**.")
433
+ except Exception as e:
434
+ await safe_send(ctx, f"❌ Kanal kilitlenemedi: {e}", ephemeral=True)
435
+
436
+ @commands.hybrid_command(name="unlock", description="Kilitlenmiş kanalın kilidini açar.")
437
+ @commands.has_permissions(manage_channels=True)
438
+ async def unlock(self, ctx: commands.Context):
439
+ if ctx.interaction:
440
+ await ctx.defer()
441
+ try:
442
+ overwrite = ctx.channel.overwrites_for(ctx.guild.default_role)
443
+ overwrite.send_messages = None
444
+ await ctx.channel.set_permissions(ctx.guild.default_role, overwrite=overwrite, reason=f"Kanal kilidi açıldı: {ctx.author}")
445
+ await safe_send(ctx, "🔓 Kanal kilidi başarıyla **açıldı**.")
446
+ except Exception as e:
447
+ await safe_send(ctx, f"❌ Kanal kilidi açılamadı: {e}", ephemeral=True)
448
+
449
+ @commands.hybrid_command(name="nuke", description="Kanalı siler ve aynı izinlerle yeniden oluşturur (Tüm mesajlar silinir).")
450
+ @commands.has_permissions(administrator=True)
451
+ async def nuke(self, ctx: commands.Context):
452
+ if ctx.interaction:
453
+ await ctx.defer(ephemeral=True)
454
+ try:
455
+ channel = ctx.channel
456
+ new_channel = await channel.clone(reason=f"Kanal nuke edildi: {ctx.author}")
457
+ await new_channel.edit(position=channel.position)
458
+ await channel.delete(reason="Nuke işlemi.")
459
+
460
+ embed = discord.Embed(
461
+ title="💥 Kanal Yeniden Oluşturuldu (Nuke)",
462
+ description=f"Bu kanal {ctx.author.mention} tarafından nuke edildi ve tüm mesajlar temizlendi.",
463
+ color=CYBER_GREEN,
464
+ timestamp=datetime.now()
465
+ )
466
+ await new_channel.send(embed=embed)
467
+ except Exception as e:
468
+ try:
469
+ await ctx.send(f"❌ Nuke işlemi başarısız: {e}", ephemeral=True)
470
+ except Exception:
471
+ pass
472
+
473
+ @commands.hybrid_command(name="banlist", description="Sunucudaki tüm yasaklı kullanıcıları listeler.")
474
+ @commands.has_permissions(ban_members=True)
475
+ async def banlist(self, ctx: commands.Context):
476
+ if ctx.interaction:
477
+ await ctx.defer(ephemeral=True)
478
+ try:
479
+ bans = [entry async for entry in ctx.guild.bans(limit=100)]
480
+ if not bans:
481
+ await safe_send(ctx, "ℹ️ Sunucuda yasaklı kullanıcı bulunmamaktadır.")
482
+ return
483
+
484
+ embed = discord.Embed(
485
+ title=f"🔨 Sunucu Ban Listesi ({len(bans)} kişi)",
486
+ color=CYBER_RED,
487
+ timestamp=datetime.now()
488
+ )
489
+
490
+ ban_text = ""
491
+ for entry in bans[:30]:
492
+ ban_text += f"• `{entry.user.name}` ({entry.user.id})\n"
493
+
494
+ embed.description = ban_text
495
+ if len(bans) > 30:
496
+ embed.set_footer(text=f"Toplam {len(bans)} yasaklı kullanıcıdan ilk 30 gösteriliyor.")
497
+
498
+ await safe_send(ctx, embed=embed)
499
+ except Exception as e:
500
+ await safe_send(ctx, f"❌ Ban listesi alınamadı: {e}", ephemeral=True)
501
+
502
+ @commands.hybrid_command(name="userinfo", description="Belirtilen üyenin profil bilgilerini gösterir.")
503
+ @app_commands.describe(member="Profil bilgisi görüntülenecek üye")
504
+ async def userinfo(self, ctx: commands.Context, member: discord.Member = None):
505
+ if ctx.interaction:
506
+ await ctx.defer()
507
+ member = member or ctx.author
508
+ try:
509
+ created_at = f"<t:{int(member.created_at.timestamp())}:F>"
510
+ joined_at = f"<t:{int(member.joined_at.timestamp())}:F>" if member.joined_at else "Bilinmiyor"
511
+ roles = [r.mention for r in member.roles if r != ctx.guild.default_role]
512
+
513
+ embed = discord.Embed(
514
+ title=f"👤 Kullanıcı Bilgisi: {member.display_name}",
515
+ color=member.color if member.color.value else CYBER_BLUE,
516
+ timestamp=datetime.now()
517
+ )
518
+ embed.set_thumbnail(url=member.display_avatar.url)
519
+ embed.add_field(name="Kullanıcı Adı", value=f"`{member.name}`", inline=True)
520
+ embed.add_field(name="ID", value=f"`{member.id}`", inline=True)
521
+ embed.add_field(name="Bot mu?", value="✅ Evet" if member.bot else "❌ Hayır", inline=True)
522
+ embed.add_field(name="Hesap Oluşturulma", value=created_at, inline=False)
523
+ embed.add_field(name="Sunucuya Katılma", value=joined_at, inline=False)
524
+ embed.add_field(name=f"Roller ({len(roles)})", value=" ".join(roles[:10]) if roles else "Rol yok", inline=False)
525
+
526
+ await safe_send(ctx, embed=embed)
527
+ except Exception as e:
528
+ await safe_send(ctx, f"❌ Kullanıcı bilgisi alınamadı: {e}", ephemeral=True)
529
+
530
+ @commands.hybrid_command(name="serverinfo", description="Sunucu bilgilerini gösterir.")
531
+ async def serverinfo(self, ctx: commands.Context):
532
+ if ctx.interaction:
533
+ await ctx.defer()
534
+ try:
535
+ guild = ctx.guild
536
+ created_at = f"<t:{int(guild.created_at.timestamp())}:F>"
537
+ bans_count = guild.me.guild_permissions.ban_members
538
+ embed = discord.Embed(
539
+ title=f"🏠 Sunucu Bilgisi: {guild.name}",
540
+ color=CYBER_BLUE,
541
+ timestamp=datetime.now()
542
+ )
543
+ if guild.icon:
544
+ embed.set_thumbnail(url=guild.icon.url)
545
+ embed.add_field(name="Sahip", value=guild.owner.mention if guild.owner else "Bilinmiyor", inline=True)
546
+ embed.add_field(name="ID", value=f"`{guild.id}`", inline=True)
547
+ embed.add_field(name="Üye Sayısı", value=f"`{guild.member_count}`", inline=True)
548
+ embed.add_field(name="Kanal Sayısı", value=f"`{len(guild.channels)}`", inline=True)
549
+ embed.add_field(name="Rol Sayısı", value=f"`{len(guild.roles)}`", inline=True)
550
+ embed.add_field(name="Boost Seviyesi", value=f"`{guild.premium_tier}`", inline=True)
551
+ embed.add_field(name="Oluşturulma Tarihi", value=created_at, inline=False)
552
+
553
+ await safe_send(ctx, embed=embed)
554
+ except Exception as e:
555
+ await safe_send(ctx, f"❌ Sunucu bilgisi alınamadı: {e}", ephemeral=True)
556
+
557
+ async def setup(bot):
558
+ await bot.add_cog(Moderation(bot))
cogs/partnership.py ADDED
@@ -0,0 +1,492 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ import re
5
+ from datetime import datetime, timedelta
6
+ from database import Database
7
+
8
+ # Premium Renkler
9
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
10
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
11
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
12
+ CYBER_DARK = discord.Color.from_rgb(44, 62, 80)
13
+
14
+ def extract_invite_link(text: str) -> str:
15
+ """Metin içindeki ilk discord davet linkini bulur"""
16
+ matches = re.findall(r"(discord\.(?:gg|com/invite)/[a-zA-Z0-9\-]+)", text, re.IGNORECASE)
17
+ if matches:
18
+ return "https://" + matches[0]
19
+ return ""
20
+
21
+ async def is_partner_blacklisted(bot, guild_id: int, text: str) -> bool:
22
+ """Metin içindeki davet linki veya sunucu ID'sinin yasaklı partner listesinde olup olmadığını kontrol eder"""
23
+ try:
24
+ records = await Database.fetch_all("SELECT identifier FROM yasakli_partnerler WHERE guild_id = ?", (guild_id,))
25
+ if not records:
26
+ return False
27
+
28
+ blacklist = [r['identifier'].strip().lower() for r in records]
29
+
30
+ # 1. Metindeki davet linklerini kontrol et ve Guild ID'lerini sorgula
31
+ links = re.findall(r"(discord\.(?:gg|com/invite)/[a-zA-Z0-9\-]+)", text, re.IGNORECASE)
32
+
33
+ for link in links:
34
+ link_clean = link.lower()
35
+ if link_clean in blacklist or f"https://{link_clean}" in blacklist:
36
+ return True
37
+
38
+ # Guild ID'sini çözmeye çalış
39
+ try:
40
+ invite = await bot.fetch_invite("https://" + link)
41
+ if invite and invite.guild:
42
+ guild_id_str = str(invite.guild.id)
43
+ if guild_id_str in blacklist:
44
+ return True
45
+ except:
46
+ pass
47
+
48
+ # 2. Metnin doğrudan içinde kara listeye alınmış sunucu ID'si veya davet linki geçiyor mu?
49
+ text_lower = text.lower()
50
+ for item in blacklist:
51
+ if item in text_lower:
52
+ return True
53
+ except Exception as e:
54
+ print(f"Partner kara liste kontrol hatası: {e}")
55
+
56
+ return False
57
+
58
+ class PartnerYaziGirisModal(discord.ui.Modal, title="Partnerlik Tanıtım Metni"):
59
+ partner_text_input = discord.ui.TextInput(
60
+ label="Sunucu Tanıtım Yazınız",
61
+ style=discord.TextStyle.long,
62
+ placeholder="Sunucunuzun tanıtım metnini ve sınırsız davet linkini buraya yapıştırın...",
63
+ required=True,
64
+ max_length=1500
65
+ )
66
+
67
+ def __init__(self, own_partner_text, yetkili_kanal_id, yetkili_rol_id, bot):
68
+ super().__init__()
69
+ self.own_partner_text = own_partner_text
70
+ self.yetkili_kanal_id = yetkili_kanal_id
71
+ self.yetkili_rol_id = yetkili_rol_id
72
+ self.bot = bot
73
+
74
+ async def on_submit(self, interaction: discord.Interaction):
75
+ # Kara liste kontrolü
76
+ is_blacklisted = await is_partner_blacklisted(self.bot, interaction.guild.id, self.partner_text_input.value)
77
+ if is_blacklisted:
78
+ await interaction.response.send_message(
79
+ content="❌ Yasaklı listedesiniz! Sunucunuz veya davet linkiniz bu sunucuda kara listeye alınmıştır.",
80
+ ephemeral=True
81
+ )
82
+ return
83
+
84
+ # 1. Sunucunun kendi partner yazısını başvuran üyenin DM kutusuna gönder
85
+ try:
86
+ if self.own_partner_text:
87
+ await interaction.user.send(
88
+ content=f"🤝 **{interaction.guild.name}** sunucusu partnerlik tanıtım metni:\n\n{self.own_partner_text}"
89
+ )
90
+ dm_status = "Sunucumuzun tanıtım metni DM kutunuza başarıyla gönderildi!"
91
+ else:
92
+ dm_status = "⚠️ Sunucumuzun tanıtım metni ayarlanmamış."
93
+ except Exception:
94
+ dm_status = "⚠️ DM kutunuz kapalı olduğu için kendi partner tanıtım metnimizi gönderemedik. Lütfen DM ayarlarınızı açın."
95
+
96
+ # 2. Yetkili kanalına onay talebi gönder
97
+ yetkili_kanal = interaction.guild.get_channel(self.yetkili_kanal_id)
98
+ if yetkili_kanal:
99
+ embed = discord.Embed(
100
+ title="📥 Yeni Partnerlik Talebi",
101
+ description=f"**Gönderen:** {interaction.user.mention} ({interaction.user.id})\n\n"
102
+ f"**Girdiği Tanıtım Metni:**\n{self.partner_text_input.value}",
103
+ color=CYBER_BLUE,
104
+ timestamp=datetime.now()
105
+ )
106
+ embed.set_thumbnail(url=interaction.user.display_avatar.url)
107
+
108
+ # Onay / Red Butonları (Persistent id'ler ile)
109
+ view = discord.ui.View(timeout=None)
110
+ view.add_item(discord.ui.Button(
111
+ label="Onayla",
112
+ style=discord.ButtonStyle.green,
113
+ custom_id=f"partner_approve_{interaction.user.id}"
114
+ ))
115
+ view.add_item(discord.ui.Button(
116
+ label="Reddet",
117
+ style=discord.ButtonStyle.red,
118
+ custom_id=f"partner_reject_{interaction.user.id}"
119
+ ))
120
+
121
+ role_mention = f"<@&{self.yetkili_rol_id}>" if self.yetkili_rol_id else ""
122
+ await yetkili_kanal.send(content=role_mention, embed=embed, view=view)
123
+
124
+ await interaction.response.send_message(
125
+ content=f"✅ Partnerlik talebiniz alındı ve yetkililere iletildi. {dm_status}",
126
+ ephemeral=True
127
+ )
128
+ else:
129
+ await interaction.response.send_message(
130
+ content="❌ Partner yetkili kanalı bulunamadı. Lütfen kuruluma başvurun.",
131
+ ephemeral=True
132
+ )
133
+
134
+ class PartnerTetikleyiciView(discord.ui.View):
135
+ def __init__(self):
136
+ super().__init__(timeout=None)
137
+
138
+ @discord.ui.button(label="Partnerlik İçin Basınız", style=discord.ButtonStyle.green, custom_id="partner_tetikleyici_btn")
139
+ async def click_partner(self, interaction: discord.Interaction, button: discord.ui.Button):
140
+ cfg = await Database.partner_ayarlarini_getir(interaction.guild.id)
141
+ if not cfg or not cfg.get("partner_kanal_id"):
142
+ await interaction.response.send_message("❌ Sunucuda partnerlik sistemi kurulumu tamamlanmamış.", ephemeral=True)
143
+ return
144
+
145
+ modal = PartnerYaziGirisModal(
146
+ cfg["partner_text"],
147
+ cfg["yetkili_kanal_id"],
148
+ cfg["yetkili_rol_id"],
149
+ interaction.client
150
+ )
151
+ await interaction.response.send_modal(modal)
152
+
153
+ class Partnership(commands.Cog):
154
+ def __init__(self, bot):
155
+ self.bot = bot
156
+
157
+ async def cog_load(self):
158
+ # Yasaklı partnerler tablosunu oluşturalım
159
+ await Database.execute("""
160
+ CREATE TABLE IF NOT EXISTS yasakli_partnerler (
161
+ guild_id INTEGER,
162
+ identifier TEXT,
163
+ PRIMARY KEY (guild_id, identifier)
164
+ )
165
+ """)
166
+
167
+ @commands.hybrid_command(name="partner-kurulum", description="Ortaklık (partnerlik) sistemi ayarlarını yapar.")
168
+ @commands.has_permissions(administrator=True)
169
+ @app_commands.describe(
170
+ partner_kanal="Onaylanan partner metinlerinin paylaşılacağı ana partner kanalı",
171
+ partner_text="Başka sunuculara gönderilecek kendi partner tanıtım yazınız",
172
+ yetkili_rol="Partner işlemlerini onaylamaya yetkili rol",
173
+ yetkili_kanal="Gelen partner taleplerinin onay butonlarıyla birlikte düşeceği kanal"
174
+ )
175
+ async def partner_kurulum(
176
+ self, ctx: commands.Context,
177
+ partner_kanal: discord.TextChannel,
178
+ partner_text: str,
179
+ yetkili_rol: discord.Role,
180
+ yetkili_kanal: discord.TextChannel
181
+ ):
182
+ if ctx.interaction:
183
+ await ctx.defer()
184
+ await Database.partner_ayarla(
185
+ ctx.guild.id,
186
+ partner_kanal.id,
187
+ partner_text,
188
+ yetkili_rol.id,
189
+ yetkili_kanal.id
190
+ )
191
+
192
+ embed = discord.Embed(
193
+ title="🤝 Partnerlik Sistemi Kuruldu",
194
+ description="Sunucu partnerlik sistemi başarıyla yapılandırıldı.",
195
+ color=CYBER_GREEN,
196
+ timestamp=datetime.now()
197
+ )
198
+ embed.add_field(name="Partner Kanalı", value=partner_kanal.mention, inline=True)
199
+ embed.add_field(name="Yetkili Kanalı", value=yetkili_kanal.mention, inline=True)
200
+ embed.add_field(name="Yetkili Rolü", value=yetkili_rol.mention, inline=True)
201
+ embed.add_field(name="Tanıtım Metnimiz", value=f"```{partner_text[:100]}...```" if len(partner_text) > 100 else f"```{partner_text}```", inline=False)
202
+
203
+ await ctx.send(embed=embed)
204
+
205
+ @commands.hybrid_command(name="partner-sıralama", description="Sunucuda yapılan partnerliklerin istatistik sıralamasını gösterir.")
206
+ @app_commands.describe(sure="Sıralama dönemi (Günlük, Haftalık, Aylık)")
207
+ @app_commands.choices(sure=[
208
+ app_commands.Choice(name="Günlük", value="günlük"),
209
+ app_commands.Choice(name="Haftalık", value="haftalık"),
210
+ app_commands.Choice(name="Aylık", value="aylık")
211
+ ])
212
+ async def partner_siralamasi(self, ctx: commands.Context, sure: str):
213
+ if ctx.interaction:
214
+ await ctx.defer()
215
+ now = datetime.now()
216
+ if sure == "günlük":
217
+ start_date = now - timedelta(days=1)
218
+ title_text = "Günlük Partner Sıralaması"
219
+ elif sure == "haftalık":
220
+ start_date = now - timedelta(days=7)
221
+ title_text = "Haftalık Partner Sıralaması"
222
+ else: # aylık
223
+ start_date = now - timedelta(days=30)
224
+ title_text = "Aylık Partner Sıralaması"
225
+
226
+ records = await Database.partner_siralamasi_getir(ctx.guild.id, start_date)
227
+ if not records:
228
+ await ctx.send(f"ℹ️ Belirtilen dönemde ({sure}) sunucuda yapılmış herhangi bir partnerlik kaydı bulunmamaktadır.")
229
+ return
230
+
231
+ embed = discord.Embed(
232
+ title=f"📈 {title_text}",
233
+ description=f"Son {sure} dönemi içerisindeki onaylanan partnerlikler ve manuel paylaşımlar baz alınmıştır.",
234
+ color=CYBER_BLUE,
235
+ timestamp=datetime.now()
236
+ )
237
+
238
+ leaderboard_str = ""
239
+ for index, record in enumerate(records[:15], start=1):
240
+ user = ctx.guild.get_member(record['user_id'])
241
+ user_mention = user.mention if user else f"Kullanıcı ID: {record['user_id']}"
242
+
243
+ emoji = "🥇" if index == 1 else "🥈" if index == 2 else "🥉" if index == 3 else f"#{index}"
244
+ leaderboard_str += f"{emoji} - {user_mention} : `{record['count']}` partnerlik\n"
245
+
246
+ embed.description = f"{embed.description}\n\n{leaderboard_str}"
247
+ await ctx.send(embed=embed)
248
+
249
+ # --- KELİME & ETİKET ALGILAMA (BUTON ATMA) ---
250
+
251
+ @commands.Cog.listener()
252
+ async def on_message(self, message: discord.Message):
253
+ if message.author.bot or not message.guild:
254
+ return
255
+ if not await Database.modul_aktif_mi(message.guild.id, "Partnership"):
256
+ return
257
+
258
+ # Ayarları kontrol et
259
+ cfg = await Database.partner_ayarlarini_getir(message.guild.id)
260
+ if not cfg or not cfg.get("partner_kanal_id"):
261
+ return
262
+
263
+ # Yetkili rol etiketlendi mi?
264
+ role_id = cfg.get("yetkili_rol_id", 0)
265
+ role_mention = f"<@&{role_id}>"
266
+
267
+ content_lower = message.content.lower()
268
+ has_role_mention = role_mention in message.content
269
+ has_keywords = any(kw in content_lower for kw in ["partner", "partnerlik", "dm"])
270
+
271
+ # Eşleşme durumunda buton gönder
272
+ if has_role_mention or has_keywords:
273
+ embed = discord.Embed(
274
+ title="🤝 Partnerlik İşlemleri",
275
+ description="Bu sunucuyla partnerlik (ortaklık) kurmak için aşağıdaki butona tıklayıp tanıtım metninizi girin. "
276
+ "Yazıyı gönderdiğinizde sunucumuzun tanıtım metni de DM kutunuza gönderilecektir.",
277
+ color=CYBER_BLUE
278
+ )
279
+ # Persistent View tetikle
280
+ view = PartnerTetikleyiciView()
281
+ await message.reply(embed=embed, view=view, mention_author=False)
282
+
283
+ # --- MANUEL TAKİP KATMANI ---
284
+ # Eğer mesaj ana partner kanalına atıldıysa ve atan kişi bot değilse
285
+ if message.channel.id == cfg.get("partner_kanal_id", 0):
286
+ # Kara liste kontrolü
287
+ is_blacklisted = await is_partner_blacklisted(self.bot, message.guild.id, message.content)
288
+ if is_blacklisted:
289
+ try:
290
+ await message.delete()
291
+ except:
292
+ pass
293
+ try:
294
+ await message.author.send(
295
+ content=f"❌ **{message.guild.name}** sunucusunun partner kanalına gönderdiğiniz metin engellendi. Yasaklı listedesiniz!"
296
+ )
297
+ except:
298
+ pass
299
+ return
300
+
301
+ invite_link = extract_invite_link(message.content)
302
+ if invite_link:
303
+ # Veritabanına manuel paylaşım olarak kaydet
304
+ await Database.partner_kaydet(
305
+ message.guild.id,
306
+ message.author.id,
307
+ None, # Yetkili onayı yok (manuel)
308
+ invite_link,
309
+ message.content,
310
+ "manual_post"
311
+ )
312
+ # Küçük bir onay emojisi bırakabiliriz
313
+ try:
314
+ await message.add_reaction("📥")
315
+ except:
316
+ pass
317
+
318
+ # --- KÜRESEL ETKİLEŞİM DİNLEYİCİ (ONAY / RED İÇİN) ---
319
+
320
+ @commands.Cog.listener()
321
+ async def on_interaction(self, interaction: discord.Interaction):
322
+ # Sadece buton tıklamaları
323
+ if interaction.type != discord.InteractionType.component:
324
+ return
325
+ if not await Database.modul_aktif_mi(interaction.guild_id, "Partnership"):
326
+ try:
327
+ await interaction.response.send_message("❌ Partnerlik modülü şu an devre dışı.", ephemeral=True)
328
+ except:
329
+ pass
330
+ return
331
+
332
+ custom_id = interaction.data.get("custom_id", "")
333
+ if not custom_id.startswith("partner_approve_") and not custom_id.startswith("partner_reject_"):
334
+ return
335
+
336
+ cfg = await Database.partner_ayarlarini_getir(interaction.guild.id)
337
+ if not cfg:
338
+ return
339
+
340
+ # Yetkili rol kontrolü
341
+ yetkili_rol_id = cfg.get("yetkili_rol_id", 0)
342
+ has_role = False
343
+ if yetkili_rol_id:
344
+ role = interaction.guild.get_role(yetkili_rol_id)
345
+ if role and role in interaction.user.roles:
346
+ has_role = True
347
+
348
+ if interaction.user.guild_permissions.administrator:
349
+ has_role = True
350
+
351
+ if not has_role:
352
+ await interaction.response.send_message(
353
+ content="❌ Bu talebi onaylama/reddetme yetkiniz bulunmamaktadır.",
354
+ ephemeral=True
355
+ )
356
+ return
357
+
358
+ # İşlemi ertele
359
+ await interaction.response.defer()
360
+
361
+ # Butonları devre dışı bırak ve mesajı güncelle
362
+ message = interaction.message
363
+ embed = message.embeds[0] if message.embeds else None
364
+ if not embed:
365
+ return
366
+
367
+ # Başvuran kullanıcıyı ve metni al
368
+ is_approve = custom_id.startswith("partner_approve_")
369
+ applicant_id = int(custom_id.split("_")[-1])
370
+
371
+ # Embed'den girdiği tanıtım metnini ayrıştır
372
+ desc = embed.description
373
+ text_indicator = "**Girdiği Tanıtım Metni:**\n"
374
+ partner_text = ""
375
+ if text_indicator in desc:
376
+ partner_text = desc.split(text_indicator)[1].strip()
377
+
378
+ if is_approve:
379
+ # Onaylandı embed güncelleme
380
+ embed.color = CYBER_GREEN
381
+ embed.title = "✅ Partnerlik Talebi Onaylandı"
382
+ embed.add_field(name="Onaylayan Yetkili", value=f"{interaction.user.mention}", inline=False)
383
+ await message.edit(embed=embed, view=None)
384
+
385
+ # Davet linkini çek
386
+ invite_link = extract_invite_link(partner_text)
387
+
388
+ # Veritabanına kaydet
389
+ await Database.partner_kaydet(
390
+ interaction.guild.id,
391
+ applicant_id,
392
+ interaction.user.id, # Onaylayan yetkili
393
+ invite_link,
394
+ partner_text,
395
+ "bot_approval"
396
+ )
397
+
398
+ # Ana partner kanalına gönder
399
+ partner_kanal_id = cfg.get("partner_kanal_id", 0)
400
+ partner_kanal = interaction.guild.get_channel(partner_kanal_id)
401
+ if partner_kanal:
402
+ applicant = interaction.guild.get_member(applicant_id)
403
+ applicant_name = applicant.mention if applicant else f"ID: {applicant_id}"
404
+
405
+ post_embed = discord.Embed(
406
+ title="🤝 Yeni Ortaklık (Partnerlik)",
407
+ description=partner_text,
408
+ color=CYBER_GREEN,
409
+ timestamp=datetime.now()
410
+ )
411
+ post_embed.set_footer(text=f"Ortak: {applicant.name if applicant else 'Bilinmeyen'} | Onaylayan Yetkili: {interaction.user.name}")
412
+ if applicant:
413
+ post_embed.set_thumbnail(url=applicant.display_avatar.url)
414
+
415
+ await partner_kanal.send(embed=post_embed)
416
+ else:
417
+ # Reddedildi embed güncelleme
418
+ embed.color = CYBER_RED
419
+ embed.title = "❌ Partnerlik Talebi Reddedildi"
420
+ embed.add_field(name="Reddeden Yetkili", value=f"{interaction.user.mention}", inline=False)
421
+ await message.edit(embed=embed, view=None)
422
+
423
+ # --- YASAKLI PARTNER YÖNETİM KOMUTLARI ---
424
+
425
+ @commands.hybrid_command(name="yasaklı-partner-ekle", description="Partner kara listesine davet linki veya sunucu ID'si ekler.")
426
+ @commands.has_permissions(administrator=True)
427
+ @app_commands.describe(deger="Yasaklanacak davet linki (örn: discord.gg/abc) veya sunucu ID'si")
428
+ async def yasakli_partner_ekle(self, ctx: commands.Context, deger: str):
429
+ if ctx.interaction:
430
+ await ctx.defer()
431
+ deger_clean = deger.strip().lower()
432
+ # Eğer davet linkiyse ve başında https:// varsa temizle
433
+ deger_clean = deger_clean.replace("https://", "").replace("http://", "")
434
+
435
+ if not deger_clean:
436
+ await ctx.send("❌ Geçersiz değer.", ephemeral=True)
437
+ return
438
+
439
+ try:
440
+ await Database.execute(
441
+ "INSERT OR IGNORE INTO yasakli_partnerler (guild_id, identifier) VALUES (?, ?)",
442
+ (ctx.guild.id, deger_clean)
443
+ )
444
+ await ctx.send(f"✅ `{deger_clean}` değeri partner kara listesine başarıyla eklendi.")
445
+ except Exception as e:
446
+ await ctx.send(f"❌ Kara listeye eklenirken veritabanı hatası: {e}", ephemeral=True)
447
+
448
+ @commands.hybrid_command(name="yasaklı-partner-çıkar", description="Partner kara listesinden davet linki veya sunucu ID'sini siler.")
449
+ @commands.has_permissions(administrator=True)
450
+ @app_commands.describe(deger="Kara listeden çıkarılacak davet linki veya sunucu ID'si")
451
+ async def yasakli_partner_cikar(self, ctx: commands.Context, deger: str):
452
+ if ctx.interaction:
453
+ await ctx.defer()
454
+ deger_clean = deger.strip().lower()
455
+ deger_clean = deger_clean.replace("https://", "").replace("http://", "")
456
+
457
+ try:
458
+ rows = await Database.execute(
459
+ "DELETE FROM yasakli_partnerler WHERE guild_id = ? AND identifier = ?",
460
+ (ctx.guild.id, deger_clean)
461
+ )
462
+ if rows > 0:
463
+ await ctx.send(f"✅ `{deger_clean}` değeri partner kara listesinden çıkarıldı.")
464
+ else:
465
+ await ctx.send(f"❌ `{deger_clean}` değeri partner kara listesinde bulunamadı.", ephemeral=True)
466
+ except Exception as e:
467
+ await ctx.send(f"❌ Kara listeden çıkarılırken veritabanı hatası: {e}", ephemeral=True)
468
+
469
+ @commands.hybrid_command(name="yasaklı-partner-liste", description="Partner kara listesindeki tüm değerleri gösterir.")
470
+ @commands.has_permissions(manage_messages=True)
471
+ async def yasakli_partner_liste(self, ctx: commands.Context):
472
+ if ctx.interaction:
473
+ await ctx.defer()
474
+ try:
475
+ records = await Database.fetch_all("SELECT identifier FROM yasakli_partnerler WHERE guild_id = ?", (ctx.guild.id,))
476
+ if not records:
477
+ await ctx.send("ℹ️ Partner kara listesinde herhangi bir değer bulunmamaktadır.")
478
+ return
479
+
480
+ list_str = "\n".join([f"- `{r['identifier']}`" for r in records])
481
+ embed = discord.Embed(
482
+ title="🛡️ Partner Kara Listesi",
483
+ description=list_str,
484
+ color=CYBER_BLUE,
485
+ timestamp=datetime.now()
486
+ )
487
+ await ctx.send(embed=embed)
488
+ except Exception as e:
489
+ await ctx.send(f"❌ Listeleme sırasında veritabanı hatası: {e}", ephemeral=True)
490
+
491
+ async def setup(bot):
492
+ await bot.add_cog(Partnership(bot))
cogs/poll.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from datetime import datetime, timedelta
5
+ import asyncio
6
+ from database import Database
7
+
8
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
9
+ CYBER_GOLD = discord.Color.from_rgb(241, 196, 15)
10
+
11
+ POLL_EMOJIS_MULTI = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"]
12
+ POLL_EMOJIS_YESNO = ["👍", "👎"]
13
+
14
+
15
+ class Poll(commands.Cog):
16
+ def __init__(self, bot):
17
+ self.bot = bot
18
+
19
+ @commands.hybrid_command(name="anket", description="Bir anket oluşturur.")
20
+ @app_commands.describe(
21
+ soru="Anket sorusu",
22
+ secenekler="Seçenekler | ile ayrılmış (en fazla 10). Boş = Evet/Hayır anketi",
23
+ sure_dakika="Anket süresi dakika cinsinden (boş = süresiz)"
24
+ )
25
+ async def anket(
26
+ self, ctx: commands.Context,
27
+ soru: str,
28
+ secenekler: str = "",
29
+ sure_dakika: int = 0
30
+ ):
31
+ if ctx.interaction:
32
+ await ctx.defer()
33
+
34
+ if secenekler:
35
+ opts = [s.strip() for s in secenekler.split(" | ") if s.strip()]
36
+ if len(opts) < 2:
37
+ await ctx.send("❌ En az 2 seçenek gereklidir. (Örn: A | B | C)", ephemeral=True)
38
+ return
39
+ if len(opts) > 10:
40
+ opts = opts[:10]
41
+
42
+ multi_emojis = POLL_EMOJIS_MULTI[:len(opts)]
43
+
44
+ embed = discord.Embed(
45
+ title="📊 Anket",
46
+ description=f"**{soru}**",
47
+ color=CYBER_BLUE,
48
+ timestamp=datetime.now()
49
+ )
50
+ embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.display_avatar.url)
51
+ for i, opt in enumerate(opts):
52
+ embed.add_field(name=f"{multi_emojis[i]} Seçenek {i+1}", value=opt, inline=False)
53
+
54
+ if sure_dakika > 0:
55
+ bitis = datetime.now() + timedelta(minutes=sure_dakika)
56
+ embed.set_footer(text=f"Anket süresi: <t:{int(bitis.timestamp())}:R> | Başlatan: {ctx.author}")
57
+ else:
58
+ embed.set_footer(text=f"Başlatan: {ctx.author}")
59
+ else:
60
+ embed = discord.Embed(
61
+ title="📊 Anket",
62
+ description=f"**{soru}**",
63
+ color=CYBER_BLUE,
64
+ timestamp=datetime.now()
65
+ )
66
+ embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.display_avatar.url)
67
+ embed.set_footer(text=f"👍 Evet | 👎 Hayır | Başlatan: {ctx.author}")
68
+
69
+ poll_msg = await ctx.send(embed=embed)
70
+
71
+ if secenekler:
72
+ for emoji in multi_emojis:
73
+ await poll_msg.add_reaction(emoji)
74
+ else:
75
+ await poll_msg.add_reaction("👍")
76
+ await poll_msg.add_reaction("👎")
77
+
78
+ if sure_dakika > 0:
79
+ await asyncio.sleep(sure_dakika * 60)
80
+ try:
81
+ final_msg = await ctx.channel.fetch_message(poll_msg.id)
82
+ results_desc = f"**Anket Süresi Doldu!**\n\n{soru}\n\n"
83
+ if secenekler:
84
+ for i, opt in enumerate(opts):
85
+ reaction_count = 0
86
+ for r in final_msg.reactions:
87
+ if str(r.emoji) == multi_emojis[i]:
88
+ reaction_count = r.count - 1
89
+ results_desc += f"{multi_emojis[i]} **{opt}**: `{reaction_count} oy`\n"
90
+ else:
91
+ yes_count = 0
92
+ no_count = 0
93
+ for r in final_msg.reactions:
94
+ if str(r.emoji) == "👍":
95
+ yes_count = r.count - 1
96
+ elif str(r.emoji) == "👎":
97
+ no_count = r.count - 1
98
+ results_desc += f"👍 **Evet**: `{yes_count} oy`\n👎 **Hayır**: `{no_count} oy`\n"
99
+
100
+ result_embed = discord.Embed(
101
+ title="📊 Anket Sonuçları",
102
+ description=results_desc,
103
+ color=CYBER_GOLD,
104
+ timestamp=datetime.now()
105
+ )
106
+ await ctx.channel.send(embed=result_embed)
107
+ except:
108
+ pass
109
+
110
+
111
+ async def setup(bot):
112
+ await bot.add_cog(Poll(bot))
cogs/qr.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from datetime import datetime
5
+ import aiohttp
6
+
7
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
8
+
9
+
10
+ class QR(commands.Cog):
11
+ def __init__(self, bot):
12
+ self.bot = bot
13
+
14
+ @commands.command(name="qr", description="Bir metin veya bağlantı için QR kod oluşturur.")
15
+ async def qr(self, ctx: commands.Context, *, metin: str):
16
+
17
+ # Google Chart API alternatifi: api.qrserver.com (ücretsiz)
18
+ import urllib.parse
19
+ encoded_text = urllib.parse.quote(metin.strip(), safe='')
20
+ qr_url = f"https://api.qrserver.com/v1/create-qr-code/?size=300x300&data={encoded_text}"
21
+
22
+ embed = discord.Embed(
23
+ title="📱 QR Kod Oluşturuldu",
24
+ description=f"**İçerik:** `{metin[:200]}`" + ("..." if len(metin) > 200 else ""),
25
+ color=CYBER_BLUE,
26
+ timestamp=datetime.now()
27
+ )
28
+ embed.set_image(url=qr_url)
29
+ embed.set_footer(text=f"İstek: {ctx.author.display_name} • ArazAI QR")
30
+
31
+ await ctx.send(embed=embed)
32
+
33
+
34
+ async def setup(bot):
35
+ await bot.add_cog(QR(bot))
cogs/reaction_roles.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands
3
+ from discord import app_commands
4
+ from datetime import datetime
5
+ from database import Database
6
+
7
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
8
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
9
+ CYBER_RED = discord.Color.from_rgb(231, 76, 60)
10
+
11
+
12
+ class ReactionRoles(commands.Cog):
13
+ def __init__(self, bot):
14
+ self.bot = bot
15
+
16
+ @commands.hybrid_command(name="reaction-rol-olustur", description="Tepki rol mesajı oluşturur.")
17
+ @commands.has_permissions(administrator=True)
18
+ @app_commands.describe(
19
+ kanal="Mesajın gönderileceği kanal",
20
+ baslik="Mesaj başlığı",
21
+ aciklama="Mesaj açıklaması"
22
+ )
23
+ async def rr_olustur(self, ctx: commands.Context, kanal: discord.TextChannel, baslik: str, aciklama: str):
24
+ if ctx.interaction:
25
+ await ctx.defer()
26
+
27
+ embed = discord.Embed(
28
+ title=baslik,
29
+ description=aciklama + "\n\n*Aşağıdaki emojilere tıklayarak rol alın.*",
30
+ color=CYBER_BLUE,
31
+ timestamp=datetime.now()
32
+ )
33
+ embed.set_footer(text="ArazAI Reaction Roles")
34
+ msg = await kanal.send(embed=embed)
35
+
36
+ # Bir yapı kurulum kullanıcıya danış: emojileri tek tek eklemesını soyle
37
+ # Yapı: her emoji bir role karsilik gelecek — kullanıcının `rr-ekle` ile eklemesi gerekiyor
38
+ await Database.execute(
39
+ "INSERT OR REPLACE INTO reaction_roller (mesaj_id, kanal_id, guild_id, emoji, rol_id, aciklama) VALUES (?, ?, ?, '', 0, '')",
40
+ (msg.id, kanal.id, ctx.guild.id)
41
+ )
42
+
43
+ await ctx.send(f"✅ Reaction rol mesajı oluşturuldu. {msg.jump_url}\nŞimdi `/reaction-rol-ekle` komutu ile emoji ve roller ekleyin!\n'll **/reaction-rol-ekle** komutunu kullanırken **mesaj_id** olarak `{msg.id}` girin.'")
44
+
45
+ @commands.hybrid_command(name="reaction-rol-ekle", description="Mevcut reaction rol mesajına emoji+rol ekler.")
46
+ @commands.has_permissions(administrator=True)
47
+ @app_commands.describe(
48
+ mesaj_id="Reaction rol mesajının ID'si",
49
+ emoji="Eklenecek emoji",
50
+ rol="Verilecek rol",
51
+ aciklama="Seçenek açıklaması (opsiyonel)"
52
+ )
53
+ async def rr_ekle(self, ctx: commands.Context, mesaj_id: str, emoji: str, rol: discord.Role, aciklama: str = ""):
54
+ if ctx.interaction:
55
+ await ctx.defer()
56
+
57
+ try:
58
+ mid = int(mesaj_id)
59
+ except:
60
+ await ctx.send("❌ Geçersiz mesaj ID.", ephemeral=True)
61
+ return
62
+
63
+ rr = await Database.fetch_one(
64
+ "SELECT * FROM reaction_roller WHERE mesaj_id = ? AND guild_id = ?",
65
+ (mid, ctx.guild.id)
66
+ )
67
+ if not rr:
68
+ await ctx.send("❌ Bu mesaj ID için bir reaction rol kaydı bulunamadı.", ephemeral=True)
69
+ return
70
+
71
+ kanal = ctx.guild.get_channel(rr['kanal_id'])
72
+ if not kanal:
73
+ await ctx.send("❌ Kanal bulunamadı.", ephemeral=True)
74
+ return
75
+
76
+ try:
77
+ msg_obj = await kanal.fetch_message(mid)
78
+ except:
79
+ await ctx.send("❌ Mesaj bulunamadı.", ephemeral=True)
80
+ return
81
+
82
+ egen = emoji.strip()
83
+
84
+ await Database.execute(
85
+ "DELETE FROM reaction_roller WHERE mesaj_id = ? AND emoji = ''",
86
+ (mid,)
87
+ )
88
+ await Database.execute(
89
+ "INSERT OR REPLACE INTO reaction_roller (mesaj_id, kanal_id, guild_id, emoji, rol_id, aciklama) VALUES (?, ?, ?, ?, ?, ?)",
90
+ (mid, rr['kanal_id'], ctx.guild.id, egen, rol.id, aciklama)
91
+ )
92
+
93
+ try:
94
+ await msg_obj.add_reaction(egen)
95
+ except:
96
+ await ctx.send(f"❌ Emoji eklenemedi: `{egen}`. Geçerli emoji girin.", ephemeral=True)
97
+ await Database.execute("DELETE FROM reaction_roller WHERE mesaj_id = ? AND emoji = ? AND rol_id = ?",
98
+ (mid, egen, rol.id))
99
+ return
100
+
101
+ # Mesaj embed'ini güncelle
102
+ all_rrs = await Database.fetch_all(
103
+ "SELECT * FROM reaction_roller WHERE mesaj_id = ? AND emoji != ''",
104
+ (mid,)
105
+ )
106
+ if msg_obj.embeds:
107
+ embed = msg_obj.embeds[0]
108
+ embed.clear_fields()
109
+ for r in all_rrs:
110
+ rr_rol = ctx.guild.get_role(r['rol_id'])
111
+ rol_name = rr_rol.mention if rr_rol else f"Rol silinmiş ({r['rol_id']})"
112
+ embed.add_field(name=f"{r['emoji']} - {r.get('aciklama', '') or 'Rol'}", value=rol_name, inline=False)
113
+ await msg_obj.edit(embed=embed)
114
+
115
+ await ctx.send(f"✅ {egen} emoji tepkisi için {rol.mention} rolü eklendi.")
116
+
117
+ @commands.hybrid_command(name="reaction-rol-sil", description="Bir reaction rol seçeneğini siler.")
118
+ @commands.has_permissions(administrator=True)
119
+ @app_commands.describe(mesaj_id="Mesaj ID", emoji="Silinecek emoji")
120
+ async def rr_sil(self, ctx: commands.Context, mesaj_id: str, emoji: str):
121
+ if ctx.interaction:
122
+ await ctx.defer()
123
+ try:
124
+ mid = int(mesaj_id)
125
+ except:
126
+ await ctx.send("❌ Geçersiz mesaj ID.", ephemeral=True)
127
+ return
128
+
129
+ rows = await Database.execute(
130
+ "DELETE FROM reaction_roller WHERE mesaj_id = ? AND guild_id = ? AND emoji = ?",
131
+ (mid, ctx.guild.id, emoji.strip())
132
+ )
133
+ if rows > 0:
134
+ await ctx.send("✅ Reaction rol silindi.")
135
+ else:
136
+ await ctx.send("❌ Bu emoji veya mesaj için bir kayıt bulunamadı.", ephemeral=True)
137
+
138
+ @commands.Cog.listener()
139
+ async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent):
140
+ if payload.user_id == self.bot.user.id:
141
+ return
142
+ if not await Database.modul_aktif_mi(payload.guild_id, "ReactionRoles"):
143
+ return
144
+ guild = self.bot.get_guild(payload.guild_id)
145
+ if not guild:
146
+ return
147
+ member = guild.get_member(payload.user_id)
148
+ if not member or member.bot:
149
+ return
150
+
151
+ emoji_str = str(payload.emoji)
152
+ rr = await Database.fetch_one(
153
+ "SELECT * FROM reaction_roller WHERE mesaj_id = ? AND emoji = ? AND guild_id = ?",
154
+ (payload.message_id, emoji_str, guild.id)
155
+ )
156
+ if not rr:
157
+ return
158
+
159
+ rol = guild.get_role(rr['rol_id'])
160
+ if not rol:
161
+ return
162
+
163
+ try:
164
+ await member.add_roles(rol, reason="Reaction rol")
165
+ except:
166
+ pass
167
+
168
+ @commands.Cog.listener()
169
+ async def on_raw_reaction_remove(self, payload: discord.RawReactionActionEvent):
170
+ if not await Database.modul_aktif_mi(payload.guild_id, "ReactionRoles"):
171
+ return
172
+ guild = self.bot.get_guild(payload.guild_id)
173
+ if not guild:
174
+ return
175
+ member = guild.get_member(payload.user_id)
176
+ if not member or member.bot:
177
+ return
178
+
179
+ emoji_str = str(payload.emoji)
180
+ rr = await Database.fetch_one(
181
+ "SELECT * FROM reaction_roller WHERE mesaj_id = ? AND emoji = ? AND guild_id = ?",
182
+ (payload.message_id, emoji_str, guild.id)
183
+ )
184
+ if not rr:
185
+ return
186
+
187
+ rol = guild.get_role(rr['rol_id'])
188
+ if not rol:
189
+ return
190
+
191
+ try:
192
+ await member.remove_roles(rol, reason="Reaction rol kaldırıldı")
193
+ except:
194
+ pass
195
+
196
+
197
+ async def setup(bot):
198
+ await bot.add_cog(ReactionRoles(bot))
cogs/reminders.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import discord
2
+ from discord.ext import commands, tasks
3
+ from discord import app_commands
4
+ from datetime import datetime
5
+ from database import Database
6
+ import re
7
+
8
+ CYBER_BLUE = discord.Color.from_rgb(52, 152, 219)
9
+ CYBER_GREEN = discord.Color.from_rgb(46, 204, 113)
10
+ CYBER_ORANGE = discord.Color.from_rgb(230, 126, 34)
11
+
12
+ def parse_reminder_duration(duration_str: str):
13
+ """Süre: '10s', '5m', '2h', '1d', '1w' -> datetime (şimdi + delta)"""
14
+ duration_str = duration_str.lower().strip()
15
+ multipliers = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400, 'w': 604800}
16
+ match = re.match(r"^(\d+)([smhdw])$", duration_str)
17
+ if not match:
18
+ return None
19
+ value = int(match.group(1))
20
+ unit = match.group(2)
21
+ from datetime import timedelta
22
+ return datetime.now() + timedelta(seconds=value * multipliers[unit])
23
+
24
+
25
+ class Reminders(commands.Cog):
26
+ def __init__(self, bot):
27
+ self.bot = bot
28
+ self.reminder_loop.start()
29
+
30
+ def cog_unload(self):
31
+ self.reminder_loop.cancel()
32
+
33
+ @tasks.loop(seconds=5)
34
+ async def reminder_loop(self):
35
+ try:
36
+ now = datetime.now()
37
+ records = await Database.fetch_all(
38
+ "SELECT * FROM hatirlaticilar WHERE bitis_zamani <= ?",
39
+ (now.isoformat(),)
40
+ )
41
+ for r in records:
42
+ guild = self.bot.get_guild(r['guild_id'])
43
+ if not guild:
44
+ await Database.execute("DELETE FROM hatirlaticilar WHERE id = ?", (r['id'],))
45
+ continue
46
+ channel = guild.get_channel(r['kanal_id'])
47
+ if not channel:
48
+ await Database.execute("DELETE FROM hatirlaticilar WHERE id = ?", (r['id'],))
49
+ continue
50
+
51
+ member = guild.get_member(r['user_id'])
52
+ mention = member.mention if member else f"<@{r['user_id']}>"
53
+
54
+ embed = discord.Embed(
55
+ title="⏰ Hatırlatıcı!",
56
+ description=f"Merhaba {mention}! Hatırlatmanız geldi:\n\n**{r['mesaj']}**",
57
+ color=CYBER_ORANGE,
58
+ timestamp=datetime.now()
59
+ )
60
+ embed.set_footer(text="ArazAI Hatırlatıcı Sistemi")
61
+ try:
62
+ await channel.send(content=mention, embed=embed)
63
+ except:
64
+ pass
65
+
66
+ await Database.execute("DELETE FROM hatirlaticilar WHERE id = ?", (r['id'],))
67
+ except Exception as e:
68
+ print(f"Reminder loop hatası: {e}")
69
+
70
+ @reminder_loop.before_loop
71
+ async def before_reminder_loop(self):
72
+ await self.bot.wait_until_ready()
73
+
74
+ @commands.hybrid_command(name="hatırlat", description="Size belirli bir süre sonra hatırlatma gönderir.")
75
+ @app_commands.describe(
76
+ sure="Süre (örn: 10s, 5m, 2h, 1d, 1w)",
77
+ mesaj="Hatırlatıcı mesajı"
78
+ )
79
+ async def hatirlat(self, ctx: commands.Context, sure: str, *, mesaj: str):
80
+ if ctx.interaction:
81
+ await ctx.defer()
82
+
83
+ bitis = parse_reminder_duration(sure)
84
+ if not bitis:
85
+ await ctx.send("❌ Geçersiz süre. Örnek: `10s`, `5m`, `2h`, `1d`, `1w`", ephemeral=True)
86
+ return
87
+
88
+ await Database.execute(
89
+ "INSERT INTO hatirlaticilar (guild_id, kanal_id, user_id, mesaj, bitis_zamani) VALUES (?, ?, ?, ?, ?)",
90
+ (ctx.guild.id, ctx.channel.id, ctx.author.id, mesaj, bitis.isoformat())
91
+ )
92
+
93
+ embed = discord.Embed(
94
+ title="✅ Hatırlatıcı Kuruldu!",
95
+ description=(
96
+ f"**Mesaj:** {mesaj}\n"
97
+ f"**Süre:** {sure}\n"
98
+ f"**Bitiş:** <t:{int(bitis.timestamp())}:F> (<t:{int(bitis.timestamp())}:R>)"
99
+ ),
100
+ color=CYBER_GREEN,
101
+ timestamp=datetime.now()
102
+ )
103
+ await ctx.send(embed=embed)
104
+
105
+ @commands.hybrid_command(name="hatırlatıcılar", description="Aktif hatırlatıcılardan ilk 10 tanesini listeler.")
106
+ async def hatirlaticilar(self, ctx: commands.Context):
107
+ if ctx.interaction:
108
+ await ctx.defer()
109
+ records = await Database.fetch_all(
110
+ "SELECT * FROM hatirlaticilar WHERE guild_id = ? AND user_id = ? ORDER BY bitis_zamani ASC LIMIT 10",
111
+ (ctx.guild.id, ctx.author.id)
112
+ )
113
+ if not records:
114
+ await ctx.send("ℹ️ Aktif hatırlatıcınız bulunmuyor.")
115
+ return
116
+
117
+ embed = discord.Embed(title="⏰ Aktif Hatırlatıcılar", color=CYBER_ORANGE, timestamp=datetime.now())
118
+ desc = ""
119
+ for r in records:
120
+ bitis = datetime.fromisoformat(r['bitis_zamani'])
121
+ desc += f"• **#{r['id']}** | <t:{int(bitis.timestamp())}:R> | {r['mesaj'][:50]}\n"
122
+ embed.description = desc
123
+ embed.set_footer(text="Silmek için: /hatırlatıcı-sil <id>")
124
+ await ctx.send(embed=embed)
125
+
126
+ @commands.hybrid_command(name="hatırlatıcı-sil", description="Bir hatırlatıcıyı ID ile siler.")
127
+ @app_commands.describe(id="Hatırlatıcı ID")
128
+ async def hatirlatici_sil(self, ctx: commands.Context, id: int):
129
+ if ctx.interaction:
130
+ await ctx.defer()
131
+ rows = await Database.execute(
132
+ "DELETE FROM hatirlaticilar WHERE id = ? AND guild_id = ? AND user_id = ?",
133
+ (id, ctx.guild.id, ctx.author.id)
134
+ )
135
+ if rows > 0:
136
+ await ctx.send(f"✅ #{id} hatırlatıcısı silindi.")
137
+ else:
138
+ await ctx.send("❌ Hatırlatıcı bulunamadı veya size ait değil.", ephemeral=True)
139
+
140
+
141
+ async def setup(bot):
142
+ await bot.add_cog(Reminders(bot))