Upload 64 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- __pycache__/database.cpython-313.pyc +0 -0
- __pycache__/main.cpython-313.pyc +0 -0
- cogs/__pycache__/ai.cpython-313.pyc +0 -0
- cogs/__pycache__/anti_raid.cpython-313.pyc +0 -0
- cogs/__pycache__/autorole.cpython-313.pyc +0 -0
- cogs/__pycache__/birthday.cpython-313.pyc +0 -0
- cogs/__pycache__/counting.cpython-313.pyc +0 -0
- cogs/__pycache__/custom_commands.cpython-313.pyc +0 -0
- cogs/__pycache__/economy.cpython-313.pyc +0 -0
- cogs/__pycache__/fun.cpython-313.pyc +0 -0
- cogs/__pycache__/giveaway.cpython-313.pyc +0 -0
- cogs/__pycache__/itibar.cpython-313.pyc +0 -0
- cogs/__pycache__/level.cpython-313.pyc +0 -0
- cogs/__pycache__/logs.cpython-313.pyc +0 -0
- cogs/__pycache__/moderation.cpython-313.pyc +0 -0
- cogs/__pycache__/partnership.cpython-313.pyc +0 -0
- cogs/__pycache__/poll.cpython-313.pyc +0 -0
- cogs/__pycache__/qr.cpython-313.pyc +0 -0
- cogs/__pycache__/reaction_roles.cpython-313.pyc +0 -0
- cogs/__pycache__/reminders.cpython-313.pyc +0 -0
- cogs/__pycache__/sistem.cpython-313.pyc +0 -0
- cogs/__pycache__/stats.cpython-313.pyc +0 -0
- cogs/__pycache__/stream_alerts.cpython-313.pyc +0 -0
- cogs/__pycache__/suggestion.cpython-313.pyc +0 -0
- cogs/__pycache__/temp_channels.cpython-313.pyc +0 -0
- cogs/__pycache__/ticket.cpython-313.pyc +0 -0
- cogs/__pycache__/timezone.cpython-313.pyc +0 -0
- cogs/__pycache__/translate.cpython-313.pyc +0 -0
- cogs/__pycache__/verification.cpython-313.pyc +0 -0
- cogs/__pycache__/weather.cpython-313.pyc +0 -0
- cogs/__pycache__/welcome.cpython-313.pyc +0 -0
- cogs/ai.py +569 -0
- cogs/anti_raid.py +842 -0
- cogs/autorole.py +68 -0
- cogs/birthday.py +119 -0
- cogs/counting.py +160 -0
- cogs/custom_commands.py +105 -0
- cogs/economy.py +364 -0
- cogs/fun.py +249 -0
- cogs/giveaway.py +251 -0
- cogs/itibar.py +551 -0
- cogs/level.py +248 -0
- cogs/logs.py +446 -0
- cogs/moderation.py +558 -0
- cogs/partnership.py +492 -0
- cogs/poll.py +112 -0
- cogs/qr.py +35 -0
- cogs/reaction_roles.py +198 -0
- 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))
|