Hilmany / cogs /ticket.py
HilmanBey's picture
Upload 64 files
cbb05ec verified
Raw
History Blame Contribute Delete
24.4 kB
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())