import discord from discord.ext import commands from discord import app_commands from datetime import datetime import asyncio import io import string import random from database import Database CYBER_BLUE = discord.Color.from_rgb(52, 152, 219) CYBER_GREEN = discord.Color.from_rgb(46, 204, 113) CYBER_RED = discord.Color.from_rgb(231, 76, 60) CYBER_ORANGE = discord.Color.from_rgb(230, 126, 34) CYBER_PURPLE = discord.Color.from_rgb(155, 89, 182) CYBER_GREY = discord.Color.from_rgb(149, 165, 166) # --- BİLET OLUŞTURMA MODALI --- class TicketCreateModal(discord.ui.Modal, title="Destek Talebi Formu"): def __init__(self, category_name, category_emoji, yetkili_rol_id, kategori_id): super().__init__() self.category_name = category_name self.category_emoji = category_emoji self.yetkili_rol_id = yetkili_rol_id self.kategori_id = kategori_id self.reason = discord.ui.TextInput( label="Destek Sebebi", style=discord.TextStyle.paragraph, placeholder="Lütfen destek talebinizin detaylarını yazınız...", required=True, max_length=500 ) self.priority = discord.ui.TextInput( label="Öncelik Durumu", style=discord.TextStyle.short, placeholder="Düşük / Normal / Yüksek / Kritik", default="Normal", required=True, max_length=20 ) self.add_item(self.reason) self.add_item(self.priority) async def on_submit(self, interaction: discord.Interaction): await interaction.response.defer(ephemeral=True) guild = interaction.guild member = interaction.user # Bilet numarası için log tablosunu sayalım row = await Database.fetch_one("SELECT COUNT(*) as count FROM ticket_log") ticket_no = (row['count'] if row else 0) + 1 # Öncelik Emojisi Belirleme prio_text = self.priority.value.strip().lower() prio_emoji = "🟡" if "düşük" in prio_text or "dusuk" in prio_text: prio_emoji = "🟢" prio_name = "Düşük" elif "yüksek" in prio_text or "yuksek" in prio_text: prio_emoji = "🟠" prio_name = "Yüksek" elif "kritik" in prio_text: prio_emoji = "🔴" prio_name = "Kritik" else: prio_name = "Normal" kategori = guild.get_channel(self.kategori_id) if self.kategori_id else None # Kanal İzinleri overwrites = { guild.default_role: discord.PermissionOverwrite(view_channel=False), member: discord.PermissionOverwrite(view_channel=True, send_messages=True, read_message_history=True, attach_files=True), guild.me: discord.PermissionOverwrite(view_channel=True, send_messages=True, read_message_history=True, manage_channels=True) } if self.yetkili_rol_id: rol = guild.get_role(self.yetkili_rol_id) if rol: overwrites[rol] = discord.PermissionOverwrite(view_channel=True, send_messages=True, read_message_history=True, manage_messages=True) # Kanalı Oluştur try: channel = await guild.create_text_channel( name=f"ticket-{ticket_no}", category=kategori, overwrites=overwrites, reason=f"Destek Talebi #{ticket_no}" ) except Exception as e: await interaction.followup.send(f"❌ Kanal oluşturulurken hata meydana geldi: {e}", ephemeral=True) return # Log kaydı await Database.execute( "INSERT INTO ticket_log (guild_id, kanal_id, user_id, durum, timestamp) VALUES (?, ?, ?, ?, ?)", (guild.id, channel.id, member.id, "acildi", datetime.now().isoformat()) ) # Embed Tasarımı embed = discord.Embed( title="Araz Ticket", description=f"{member.mention} tarafından ticket talebi **{datetime.now().strftime('%d.%m.%Y %H:%M')}** tarihinde oluşturuldu. <@&{self.yetkili_rol_id}> rolüne sahip yetkililer sizinle ilgilenecek.\n\n**Bilet Açılış Nedeni:**\n{self.reason.value}", color=CYBER_BLUE, timestamp=datetime.now() ) if interaction.client.user.display_avatar: embed.set_thumbnail(url=interaction.client.user.display_avatar.url) embed.add_field(name="Destek ID", value=f"#{ticket_no}", inline=True) embed.add_field(name="Destek Kategorisi", value=f"{self.category_emoji} {self.category_name}", inline=True) embed.add_field(name="Öncelik", value=f"{prio_emoji} {prio_name}", inline=True) embed.add_field(name="Ortalama Yanıt Süresi", value="1 dakika - 2 dakika", inline=False) embed.add_field(name="Destek Durumu", value="⚪ Yetkili bekleniyor...", inline=False) # Bildirim ve Etiketleme mentions = f"{member.mention}" if self.yetkili_rol_id: mentions += f" <@&{self.yetkili_rol_id}>" view = TicketControlView() await channel.send(content=mentions, embed=embed, view=view) await interaction.followup.send(f"✅ Destek talebiniz oluşturuldu: {channel.mention}", ephemeral=True) # --- TICKET PANEL BÜTONLARI (GİRİŞ PANELDEN SEÇİM) --- class TicketPanelView(discord.ui.View): def __init__(self): super().__init__(timeout=None) @discord.ui.button(label="Genel Destek", style=discord.ButtonStyle.primary, custom_id="ticket_cat_genel", emoji="📩") async def cat_genel(self, interaction: discord.Interaction, button: discord.ui.Button): cfg = await Database.fetch_one("SELECT * FROM ticket_ayarlar WHERE guild_id = ?", (interaction.guild.id,)) if not cfg or not cfg.get("aktif"): await interaction.response.send_message("❌ Ticket sistemi bu sunucuda aktif değil.", ephemeral=True) return modal = TicketCreateModal("Genel Destek", "📩", cfg.get("yetkili_rol_id"), cfg.get("kategori_id")) await interaction.response.send_modal(modal) @discord.ui.button(label="Yetkili Alım", style=discord.ButtonStyle.success, custom_id="ticket_cat_yetkili", emoji="🔥") async def cat_yetkili(self, interaction: discord.Interaction, button: discord.ui.Button): cfg = await Database.fetch_one("SELECT * FROM ticket_ayarlar WHERE guild_id = ?", (interaction.guild.id,)) if not cfg or not cfg.get("aktif"): await interaction.response.send_message("❌ Ticket sistemi bu sunucuda aktif değil.", ephemeral=True) return modal = TicketCreateModal("Yetkili Alım", "🔥", cfg.get("yetkili_rol_id"), cfg.get("kategori_id")) await interaction.response.send_modal(modal) @discord.ui.button(label="Ortaklık & Sponsorluk", style=discord.ButtonStyle.secondary, custom_id="ticket_cat_ortaklik", emoji="🤝") async def cat_ortaklik(self, interaction: discord.Interaction, button: discord.ui.Button): cfg = await Database.fetch_one("SELECT * FROM ticket_ayarlar WHERE guild_id = ?", (interaction.guild.id,)) if not cfg or not cfg.get("aktif"): await interaction.response.send_message("❌ Ticket sistemi bu sunucuda aktif değil.", ephemeral=True) return modal = TicketCreateModal("Ortaklık & Sponsorluk", "🤝", cfg.get("yetkili_rol_id"), cfg.get("kategori_id")) await interaction.response.send_modal(modal) # --- ÖNCELİK DEĞİŞTİRME SELECT MENU --- class PrioritySelect(discord.ui.Select): def __init__(self): options = [ discord.SelectOption(label="Düşük", value="Düşük", emoji="🟢"), discord.SelectOption(label="Normal", value="Normal", emoji="🟡"), discord.SelectOption(label="Yüksek", value="Yüksek", emoji="🟠"), discord.SelectOption(label="Kritik", value="Kritik", emoji="🔴") ] super().__init__(placeholder="Öncelik seviyesini değiştir...", min_values=1, max_values=1, options=options, custom_id="ticket_prio_select") async def callback(self, interaction: discord.Interaction): embed = interaction.message.embeds[0] prio_val = self.values[0] prio_emojis = { "Düşük": "🟢", "Normal": "🟡", "Yüksek": "🟠", "Kritik": "🔴" } # Öncelik field alanını güncelle for idx, field in enumerate(embed.fields): if field.name == "Öncelik": embed.set_field_at(idx, name="Öncelik", value=f"{prio_emojis[prio_val]} {prio_val}", inline=True) break await interaction.response.edit_message(embed=embed) # --- KANALA EKLENECEK ÜYE MODALI --- class ManageMembersModal(discord.ui.Modal, title="Üye Ekle / Çıkar"): def __init__(self, action): super().__init__() self.action = action # "add" or "remove" self.user_id = discord.ui.TextInput( label="Kullanıcı ID veya Mention", placeholder="Eklenecek veya çıkarılacak üyenin ID numarasını giriniz...", required=True ) self.add_item(self.user_id) async def on_submit(self, interaction: discord.Interaction): # Üyeyi bulmaya çalışalım val = self.user_id.value.strip() uid = "" for char in val: if char.isdigit(): uid += char try: member_id = int(uid) member = interaction.guild.get_member(member_id) if not member: member = await interaction.guild.fetch_member(member_id) except: await interaction.response.send_message("❌ Geçersiz kullanıcı ID girdiniz.", ephemeral=True) return if self.action == "add": await interaction.channel.set_permissions(member, view_channel=True, send_messages=True, read_message_history=True, attach_files=True) await interaction.response.send_message(f"✅ {member.mention} destek kanalına başarıyla eklendi.", ephemeral=False) else: await interaction.channel.set_permissions(member, overwrite=None) await interaction.response.send_message(f"❌ {member.mention} destek kanalından çıkarıldı.", ephemeral=False) # --- KANALA DEVRETME MODALI --- class AssignTicketModal(discord.ui.Modal, title="Talebi Devret"): def __init__(self): super().__init__() self.target = discord.ui.TextInput( label="Yetkili Kullanıcı veya Rol ID", placeholder="Talebin devredileceği kişinin veya rolün ID numarasını yazınız...", required=True ) self.add_item(self.target) async def on_submit(self, interaction: discord.Interaction): val = self.target.value.strip() target_id = "" for char in val: if char.isdigit(): target_id += char try: tid = int(target_id) role_or_member = interaction.guild.get_role(tid) or interaction.guild.get_member(tid) if not role_or_member: role_or_member = await interaction.guild.fetch_member(tid) except: await interaction.response.send_message("❌ Geçersiz Kullanıcı veya Rol ID.", ephemeral=True) return await interaction.channel.set_permissions(role_or_member, view_channel=True, send_messages=True, read_message_history=True, manage_messages=True) await interaction.response.send_message(f"🔄 Destek talebi başarıyla {role_or_member.mention} üzerine devredildi.", ephemeral=False) # --- İÇ NOT MODALI --- class InternalNoteModal(discord.ui.Modal, title="İç Not Ekle"): def __init__(self): super().__init__() self.note = discord.ui.TextInput( label="Notunuz", style=discord.TextStyle.paragraph, placeholder="Sadece yetkililerin göreceği dahili bir not yazın...", required=True ) self.add_item(self.note) async def on_submit(self, interaction: discord.Interaction): embed = discord.Embed( title="📝 Dahili Yetkili Notu", description=self.note.value, color=CYBER_PURPLE, timestamp=datetime.now() ) embed.set_footer(text=f"Notu Eklendi: {interaction.user.display_name}") await interaction.response.send_message(embed=embed) # --- BİLET KONTROL PANELİ VİEW (2. ve 3. Ekran Görüntüsü) --- class TicketControlView(discord.ui.View): def __init__(self): super().__init__(timeout=None) self.add_item(PrioritySelect()) @discord.ui.button(label="Ticket Claim", style=discord.ButtonStyle.primary, custom_id="ticket_ctrl_claim", emoji="📥", row=0) async def btn_claim(self, interaction: discord.Interaction, button: discord.ui.Button): embed = interaction.message.embeds[0] # Durumu güncelle for idx, field in enumerate(embed.fields): if field.name == "Destek Durumu": embed.set_field_at(idx, name="Destek Durumu", value=f"📥 Alındı (Yetkili: {interaction.user.mention})", inline=False) break await interaction.response.edit_message(embed=embed) await interaction.followup.send(f"📥 {interaction.user.mention} talebi üstlendi.", ephemeral=False) @discord.ui.button(label="Beklemede", style=discord.ButtonStyle.danger, custom_id="ticket_ctrl_pending", emoji="🔴", row=0) async def btn_pending(self, interaction: discord.Interaction, button: discord.ui.Button): embed = interaction.message.embeds[0] for idx, field in enumerate(embed.fields): if field.name == "Destek Durumu": embed.set_field_at(idx, name="Destek Durumu", value="🔴 Beklemede...", inline=False) break await interaction.response.edit_message(embed=embed) @discord.ui.button(label="İnceleniyor", style=discord.ButtonStyle.secondary, custom_id="ticket_ctrl_investigating", emoji="🔍", row=0) async def btn_investigating(self, interaction: discord.Interaction, button: discord.ui.Button): embed = interaction.message.embeds[0] for idx, field in enumerate(embed.fields): if field.name == "Destek Durumu": embed.set_field_at(idx, name="Destek Durumu", value="🔍 İnceleniyor...", inline=False) break await interaction.response.edit_message(embed=embed) @discord.ui.button(label="Çözüldü", style=discord.ButtonStyle.success, custom_id="ticket_ctrl_solved", emoji="✅", row=0) async def btn_solved(self, interaction: discord.Interaction, button: discord.ui.Button): embed = interaction.message.embeds[0] for idx, field in enumerate(embed.fields): if field.name == "Destek Durumu": embed.set_field_at(idx, name="Destek Durumu", value="✅ Çözüldü.", inline=False) break await interaction.response.edit_message(embed=embed) @discord.ui.button(style=discord.ButtonStyle.secondary, custom_id="ticket_ctrl_reset", emoji="🔄", row=0) async def btn_reset(self, interaction: discord.Interaction, button: discord.ui.Button): embed = interaction.message.embeds[0] for idx, field in enumerate(embed.fields): if field.name == "Destek Durumu": embed.set_field_at(idx, name="Destek Durumu", value="⚪ Yetkili bekleniyor...", inline=False) break await interaction.response.edit_message(embed=embed) @discord.ui.button(label="Talebi Kapat", style=discord.ButtonStyle.danger, custom_id="ticket_ctrl_close", emoji="🗑️", row=1) async def btn_close(self, interaction: discord.Interaction, button: discord.ui.Button): # Kapatma log kaydı await Database.execute( "INSERT INTO ticket_log (guild_id, kanal_id, user_id, durum, timestamp) VALUES (?, ?, ?, ?, ?)", (interaction.guild.id, interaction.channel.id, interaction.user.id, "kapandi", datetime.now().isoformat()) ) await interaction.response.send_message("🔒 Ticket kapatılıyor... Kanal **5 saniye** sonra tamamen silinecek.") await asyncio.sleep(5) try: await interaction.channel.delete(reason=f"Ticket kapatıldı: {interaction.user}") except: pass @discord.ui.button(label="Bildirim Al", style=discord.ButtonStyle.secondary, custom_id="ticket_ctrl_notif", emoji="🔔", row=1) async def btn_notif(self, interaction: discord.Interaction, button: discord.ui.Button): # Kullanıcıyı kanaldan bahsetme listesi veya basit bildirim onayı await interaction.response.send_message("🔔 Bilet güncellemelerinde size bildirim gönderilecek şekilde işaretlendi.", ephemeral=True) @discord.ui.button(label="Üyeleri Yönet", style=discord.ButtonStyle.secondary, custom_id="ticket_ctrl_members", emoji="👤", row=1) async def btn_members(self, interaction: discord.Interaction, button: discord.ui.Button): # Üye Ekleme / Çıkarma seçimi için buton view gönderelim select_view = discord.ui.View(timeout=60) btn_add = discord.ui.Button(label="Üye Ekle", style=discord.ButtonStyle.success, emoji="➕") btn_rem = discord.ui.Button(label="Üye Çıkar", style=discord.ButtonStyle.danger, emoji="➖") async def add_cb(inter): await inter.response.send_modal(ManageMembersModal("add")) async def rem_cb(inter): await inter.response.send_modal(ManageMembersModal("remove")) btn_add.callback = add_cb btn_rem.callback = rem_cb select_view.add_item(btn_add) select_view.add_item(btn_rem) await interaction.response.send_message("Lütfen yapmak istediğiniz işlemi seçin:", view=select_view, ephemeral=True) @discord.ui.button(label="Transcript", style=discord.ButtonStyle.secondary, custom_id="ticket_ctrl_transcript", emoji="📄", row=1) async def btn_transcript(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.defer() # Kanal mesaj geçmişini okuyalım chat_log = [] async for message in interaction.channel.history(limit=500, oldest_first=True): time_str = message.created_at.strftime("%d.%m.%Y %H:%M") chat_log.append(f"[{time_str}] {message.author.name}: {message.content}") log_content = "\n".join(chat_log) file_data = io.BytesIO(log_content.encode("utf-8")) discord_file = discord.File(fp=file_data, filename=f"transcript-{interaction.channel.name}.txt") # Log kanalı ayarlanmışsa oraya da gönder cfg = await Database.fetch_one("SELECT log_kanal_id FROM ticket_ayarlar WHERE guild_id = ?", (interaction.guild.id,)) if cfg and cfg.get('log_kanal_id'): log_chan = interaction.guild.get_channel(cfg['log_kanal_id']) if log_chan: try: await log_chan.send(content=f"📄 **{interaction.channel.name}** biletine ait konuşma geçmişi:", file=discord_file) file_data.seek(0) except: pass await interaction.followup.send(content="📄 Konuşma dökümü (Transcript) oluşturuldu:", file=discord_file) @discord.ui.button(label="Devret", style=discord.ButtonStyle.secondary, custom_id="ticket_ctrl_assign", emoji="🔄", row=2) async def btn_assign(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_modal(AssignTicketModal()) @discord.ui.button(label="İç Not", style=discord.ButtonStyle.secondary, custom_id="ticket_ctrl_internal", emoji="📝", row=2) async def btn_internal(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_modal(InternalNoteModal()) class Ticket(commands.Cog): def __init__(self, bot): self.bot = bot @commands.hybrid_command(name="ticket-kurulum", description="Ticket sistemini kurar.") @commands.has_permissions(administrator=True) @app_commands.describe( kategori="Ticket kanallarının oluşturulacağı kategori", yetkili_rol="Ticket'lara erişimi olacak yetkili rol", log_kanal="Ticket log kanalı", panel_kanal="Ticket panel butonunun gönderileceği kanal" ) async def ticket_kurulum( self, ctx: commands.Context, kategori: discord.CategoryChannel, yetkili_rol: discord.Role, log_kanal: discord.TextChannel, panel_kanal: discord.TextChannel ): if ctx.interaction: await ctx.defer() await Database.execute( """INSERT OR REPLACE INTO ticket_ayarlar (guild_id, kategori_id, log_kanal_id, yetkili_rol_id, panel_kanal_id, aktif) VALUES (?, ?, ?, ?, ?, 1)""", (ctx.guild.id, kategori.id, log_kanal.id, yetkili_rol.id, panel_kanal.id) ) # Panel mesajı gönder (1. Ekran Görüntüsü tasarımı) embed = discord.Embed( title="🛠️ Destek Talebi Oluştur", description="Bilet oluşturmadan önce aşağıdaki kuralları okuyunuz. Aşağıdaki butonlardan size uygun kategoriyi seçerek destek talebi oluşturabilirsiniz.\n\n" "**Genel Kurallar:**\n" "• Biletinizi **24 saat** içerisinde kontrol etmek zorundasınız.\n" "• Yönetim üyelerine özel mesaj atmaktan kaçının.\n" "• Sorunlarınızı bilet aracılığıyla iletişim kurun.\n\n" "**Sorun Bildirimi:**\n" "• Sorunu detaylı bir şekilde açıklayın.\n" "• Gerekli bilgileri eksiksiz paylaşın.\n\n" "3 kategori mevcut • Ortalama yanıt süresi: 1 dakika - 2 dakika", color=CYBER_BLUE, timestamp=datetime.now() ) embed.set_author(name="Araz Ticket", icon_url=ctx.guild.icon.url if ctx.guild.icon else None) embed.set_thumbnail(url=ctx.guild.icon.url if ctx.guild.icon else "") view = TicketPanelView() panel_msg = await panel_kanal.send(embed=embed, view=view) await Database.execute( "UPDATE ticket_ayarlar SET panel_mesaj_id = ? WHERE guild_id = ?", (panel_msg.id, ctx.guild.id) ) await ctx.send(f"✅ Ticket sistemi kuruldu! Panel {panel_kanal.mention} kanalına gönderildi.") @commands.hybrid_command(name="ticket-kapat", description="Ticket sistemini devre dışı bırakır.") @commands.has_permissions(administrator=True) async def ticket_kapat(self, ctx: commands.Context): if ctx.interaction: await ctx.defer() await Database.execute("UPDATE ticket_ayarlar SET aktif = 0 WHERE guild_id = ?", (ctx.guild.id,)) await ctx.send("✅ Ticket sistemi devre dışı bırakıldı.") @commands.hybrid_command(name="ticket-log", description="Ticket log kayıtlarını gösterir.") @commands.has_permissions(manage_guild=True) async def ticket_log(self, ctx: commands.Context): if ctx.interaction: await ctx.defer() records = await Database.fetch_all( "SELECT * FROM ticket_log WHERE guild_id = ? ORDER BY id DESC LIMIT 20", (ctx.guild.id,) ) if not records: await ctx.send("ℹ️ Ticket log kaydı bulunmamaktadır.") return embed = discord.Embed(title="🎫 Ticket Log Kayıtları", color=CYBER_BLUE, timestamp=datetime.now()) log_str = "" for r in records: tarih = datetime.fromisoformat(r['timestamp']).strftime("%d.%m.%Y %H:%M") log_str += f"• `{tarih}` | <@{r['user_id']}> | Kanal: `{r['kanal_id']}` | **{r['durum']}**\n" embed.description = log_str[:4000] await ctx.send(embed=embed) async def setup(bot): await bot.add_cog(Ticket(bot)) bot.add_view(TicketPanelView()) bot.add_view(TicketControlView())