Spaces:
Sleeping
Sleeping
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +304 -38
src/streamlit_app.py
CHANGED
|
@@ -1,40 +1,306 @@
|
|
| 1 |
-
import altair as alt
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
import streamlit as st
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
""
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
"
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
+
import openai
|
| 3 |
+
from dotenv import load_dotenv
|
| 4 |
+
import os
|
| 5 |
+
from docx import Document
|
| 6 |
+
from fpdf import FPDF
|
| 7 |
+
from io import BytesIO
|
| 8 |
+
import pyrebase
|
| 9 |
+
import requests
|
| 10 |
+
import json
|
| 11 |
|
| 12 |
+
load_dotenv()
|
| 13 |
+
|
| 14 |
+
# ✅ Multiple AI Provider Support
|
| 15 |
+
AI_PROVIDERS = {
|
| 16 |
+
"OpenAI": {
|
| 17 |
+
"api_key": os.getenv("OPENAI_API_KEY"),
|
| 18 |
+
"available": bool(os.getenv("OPENAI_API_KEY"))
|
| 19 |
+
},
|
| 20 |
+
"Hugging Face (FREE)": {
|
| 21 |
+
"api_key": os.getenv("HUGGINGFACE_API_KEY"),
|
| 22 |
+
"available": True # Ücretsiz tier var
|
| 23 |
+
},
|
| 24 |
+
"Groq (FREE)": {
|
| 25 |
+
"api_key": os.getenv("GROQ_API_KEY"),
|
| 26 |
+
"available": True # Ücretsiz tier var
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
# OpenAI client (eğer varsa)
|
| 31 |
+
if AI_PROVIDERS["OpenAI"]["available"]:
|
| 32 |
+
openai.api_key = os.getenv("OPENAI_API_KEY")
|
| 33 |
+
client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
| 34 |
+
else:
|
| 35 |
+
client = None
|
| 36 |
+
|
| 37 |
+
# ✅ Firebase Config
|
| 38 |
+
firebaseConfig = {
|
| 39 |
+
"apiKey": "AIzaSyB9tHRV1jgMKwtwewOhPT555Lh1Lh-31Pc",
|
| 40 |
+
"authDomain": "blogcraftt.firebaseapp.com",
|
| 41 |
+
"projectId": "blogcraftt",
|
| 42 |
+
"storageBucket": "blogcraftt.appspot.com",
|
| 43 |
+
"messagingSenderId": "708843280683",
|
| 44 |
+
"appId": "1:708843280683:web:1ab9d956168b5ad5b89d6e",
|
| 45 |
+
"databaseURL": ""
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
firebase = pyrebase.initialize_app(firebaseConfig)
|
| 49 |
+
auth = firebase.auth()
|
| 50 |
+
|
| 51 |
+
# ✅ Prompt Library Tanımlaması
|
| 52 |
+
prompt_library = {
|
| 53 |
+
"📝 Blog Yazısı": "Sen profesyonel bir Türk blog yazarı ve içerik üreticisisin. Türkçe olarak başlangıç, gelişme ve sonuç yapısıyla en az 1000 kelimelik özgün ve SEO uyumlu bir blog yaz. Başlıklar kullan ve düzenli bir yapıda sun. Yanıtını tamamen Türkçe ver.",
|
| 54 |
+
|
| 55 |
+
"🔗 LinkedIn Post": """Sen LinkedIn'de başarılı bir Türk profesyonel içerik üreticisisin. Türkçe olarak etkili LinkedIn postları yazarsın. Kuralların:
|
| 56 |
+
1. İlgi çekici bir Türkçe hook ile başla
|
| 57 |
+
2. Değerli bilgi ver
|
| 58 |
+
3. Paragraflar kısa olsun (2-3 cümle max)
|
| 59 |
+
4. Emojiler kullan ama abartma
|
| 60 |
+
5. Sonunda engaging bir Türkçe soru sor
|
| 61 |
+
6. İlgili Türkçe hashtagleri ekle
|
| 62 |
+
7. Profesyonel ama samimi bir Türkçe ton kullan
|
| 63 |
+
8. 150-200 kelime arasında tut
|
| 64 |
+
Yanıtını tamamen Türkçe ver.""",
|
| 65 |
+
|
| 66 |
+
"🐦 Tweet": "Sen yaratıcı bir Türk içerik üreticisisin. 280 karakteri geçmeyen, kısa, vurucu ve etkileyici bir Türkçe tweet hazırla. Türkçe hashtag kullan ve viral olabilecek şekilde yaz. Yanıtını tamamen Türkçe ver.",
|
| 67 |
+
|
| 68 |
+
"🛍 Ürün Tanıtımı": "Türk pazarlama uzmanı gibi Türkçe ürün tanıtımı yap. Sorun-çözüm odaklı Türkçe yaz. Ürünün faydalarını vurgula ve satış odaklı Türkçe içerik üret. Yanıtını tamamen Türkçe ver.",
|
| 69 |
+
|
| 70 |
+
"📧 Email Marketing": "Profesyonel Türk email pazarlama uzmanısın. Türkçe konu başlığından başlayarak, açılış oranı yüksek, dönüşüm odaklı Türkçe email içeriği yaz. Yanıtını tamamen Türkçe ver.",
|
| 71 |
+
|
| 72 |
+
"🎥 Video Script": "Türk video içerik üreticisisin. Hook, içerik ve call-to-action yapısıyla Türkçe video script'i hazırla. Dakika başına 150 kelime hesabıyla Türkçe yaz. Yanıtını tamamen Türkçe ver."
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
st.set_page_config(page_title="AI Content Factory - Login", layout="wide")
|
| 76 |
+
st.title("🔐 AI Content Factory")
|
| 77 |
+
|
| 78 |
+
# Kullanıcı oturumu
|
| 79 |
+
if 'user' not in st.session_state:
|
| 80 |
+
st.session_state['user'] = None
|
| 81 |
+
|
| 82 |
+
# Email & Şifre Inputları
|
| 83 |
+
st.sidebar.header("User Authentication")
|
| 84 |
+
email = st.sidebar.text_input("📧 Email")
|
| 85 |
+
password = st.sidebar.text_input("🔑 Password", type="password")
|
| 86 |
+
|
| 87 |
+
col1, col2 = st.sidebar.columns(2)
|
| 88 |
+
|
| 89 |
+
with col1:
|
| 90 |
+
if st.sidebar.button("Login"):
|
| 91 |
+
try:
|
| 92 |
+
user = auth.sign_in_with_email_and_password(email, password)
|
| 93 |
+
st.session_state['user'] = user
|
| 94 |
+
st.sidebar.success(f"✅ Welcome {email}")
|
| 95 |
+
except:
|
| 96 |
+
st.sidebar.error("❌ Invalid email or password")
|
| 97 |
+
|
| 98 |
+
with col2:
|
| 99 |
+
if st.sidebar.button("Sign Up"):
|
| 100 |
+
try:
|
| 101 |
+
user = auth.create_user_with_email_and_password(email, password)
|
| 102 |
+
st.sidebar.success("✅ Account created successfully! Please log in.")
|
| 103 |
+
except:
|
| 104 |
+
st.sidebar.error("❌ Failed to register. Email may already be in use or password is weak.")
|
| 105 |
+
|
| 106 |
+
# Oturum varsa logout göster
|
| 107 |
+
if st.session_state['user']:
|
| 108 |
+
if st.sidebar.button("Logout"):
|
| 109 |
+
st.session_state['user'] = None
|
| 110 |
+
st.sidebar.success("✅ Logged out successfully")
|
| 111 |
+
|
| 112 |
+
# Content Generation Section
|
| 113 |
+
if st.session_state['user']:
|
| 114 |
+
st.subheader("✨ Welcome to AI Content Generation")
|
| 115 |
+
|
| 116 |
+
# ✅ Prompt Library Seçici
|
| 117 |
+
st.sidebar.title("📚 Prompt Library")
|
| 118 |
+
selected_prompt_category = st.sidebar.selectbox("Kategori Seç", list(prompt_library.keys()))
|
| 119 |
+
default_prompt = prompt_library[selected_prompt_category]
|
| 120 |
+
custom_prompt = st.sidebar.text_area("✏️ Promptu Düzenle veya Yaz", value=default_prompt, height=150)
|
| 121 |
+
|
| 122 |
+
# ✅ Prompt Kaydetme (Demo)
|
| 123 |
+
if st.sidebar.button("💾 Promptu Kaydet (Demo)"):
|
| 124 |
+
st.sidebar.success("✅ Prompt kaydedildi (demo)")
|
| 125 |
+
|
| 126 |
+
st.sidebar.markdown("---")
|
| 127 |
+
|
| 128 |
+
# Blog Detail Inputs
|
| 129 |
+
st.sidebar.title("Input your Blog Detail")
|
| 130 |
+
st.sidebar.subheader("Enter details of the Blog You want to generate")
|
| 131 |
+
|
| 132 |
+
# Blog title
|
| 133 |
+
blog_title = st.sidebar.text_input("Blog Title")
|
| 134 |
+
|
| 135 |
+
# Keywords input
|
| 136 |
+
keywords = st.sidebar.text_area("Keywords (comma-seperated)")
|
| 137 |
+
|
| 138 |
+
num_words = st.sidebar.slider("Number of words", min_value=250, max_value=1000, step=250)
|
| 139 |
+
|
| 140 |
+
submit_button = st.sidebar.button("Generate")
|
| 141 |
+
|
| 142 |
+
# Ana içerik üretimi
|
| 143 |
+
if submit_button and keywords:
|
| 144 |
+
|
| 145 |
+
# Seçilen promptu al
|
| 146 |
+
final_prompt = custom_prompt.strip()
|
| 147 |
+
|
| 148 |
+
# Öncelik sırası: Groq > Hugging Face (backend'de saklı key'ler)
|
| 149 |
+
current_groq_key = os.getenv("GROQ_API_KEY", "")
|
| 150 |
+
current_hf_key = os.getenv("HUGGINGFACE_API_KEY", "")
|
| 151 |
+
|
| 152 |
+
selected_service = None
|
| 153 |
+
if current_groq_key:
|
| 154 |
+
selected_service = "Groq"
|
| 155 |
+
elif current_hf_key:
|
| 156 |
+
selected_service = "Hugging Face"
|
| 157 |
+
else:
|
| 158 |
+
st.error("⚠️ Sistem yapılandırması eksik. Lütfen yönetici ile iletişime geçin.")
|
| 159 |
+
st.stop()
|
| 160 |
+
|
| 161 |
+
st.info(f"🤖 {selected_service} servisi kullanılıyor...")
|
| 162 |
+
|
| 163 |
+
# API Çağrısı yapacak fonksiyon
|
| 164 |
+
def make_ai_request(prompt, user_message, max_tokens=2048):
|
| 165 |
+
if selected_service == "Groq":
|
| 166 |
+
try:
|
| 167 |
+
url = "https://api.groq.com/openai/v1/chat/completions"
|
| 168 |
+
headers = {
|
| 169 |
+
"Authorization": f"Bearer {current_groq_key}",
|
| 170 |
+
"Content-Type": "application/json"
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
data = {
|
| 174 |
+
"model": "llama3-8b-8192",
|
| 175 |
+
"messages": [
|
| 176 |
+
{"role": "system", "content": prompt},
|
| 177 |
+
{"role": "user", "content": user_message}
|
| 178 |
+
],
|
| 179 |
+
"max_tokens": min(max_tokens, 2048),
|
| 180 |
+
"temperature": 0.7
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
response = requests.post(url, headers=headers, json=data)
|
| 184 |
+
if response.status_code == 200:
|
| 185 |
+
result = response.json()
|
| 186 |
+
return result['choices'][0]['message']['content']
|
| 187 |
+
else:
|
| 188 |
+
st.warning(f"Groq API hatası, Hugging Face'e geçiliyor...")
|
| 189 |
+
return fallback_to_hf(prompt, user_message)
|
| 190 |
+
except Exception as e:
|
| 191 |
+
st.warning(f"Groq bağlantı hatası, Hugging Face'e geçiliyor...")
|
| 192 |
+
return fallback_to_hf(prompt, user_message)
|
| 193 |
+
|
| 194 |
+
elif selected_service == "Hugging Face":
|
| 195 |
+
return fallback_to_hf(prompt, user_message)
|
| 196 |
+
|
| 197 |
+
return None
|
| 198 |
+
|
| 199 |
+
def fallback_to_hf(prompt, user_message):
|
| 200 |
+
try:
|
| 201 |
+
API_URL = "https://api-inference.huggingface.co/models/meta-llama/Llama-2-7b-chat-hf"
|
| 202 |
+
headers = {"Authorization": f"Bearer {current_hf_key}"}
|
| 203 |
+
|
| 204 |
+
full_prompt = f"{prompt}\n\nUser: {user_message}\nAssistant: Türkçe olarak yanıtlayacağım:"
|
| 205 |
+
payload = {"inputs": full_prompt[:1000], "parameters": {"max_new_tokens": 500}}
|
| 206 |
+
|
| 207 |
+
response = requests.post(API_URL, headers=headers, json=payload)
|
| 208 |
+
|
| 209 |
+
if response.status_code == 200:
|
| 210 |
+
result = response.json()
|
| 211 |
+
if isinstance(result, list) and len(result) > 0:
|
| 212 |
+
generated_text = result[0].get('generated_text', '')
|
| 213 |
+
if "Assistant:" in generated_text:
|
| 214 |
+
return generated_text.split("Assistant:")[-1].strip()
|
| 215 |
+
return generated_text
|
| 216 |
+
return f"HuggingFace ile basit Türkçe içerik: {user_message[:100]}..."
|
| 217 |
+
else:
|
| 218 |
+
return f"📝 Template içerik: {keywords} konusunda {selected_prompt_category} formatında profesyonel Türkçe içerik."
|
| 219 |
+
except Exception as e:
|
| 220 |
+
return f"📝 Template içerik: {keywords} konusunda {selected_prompt_category} formatında profesyonel Türkçe içerik."
|
| 221 |
+
|
| 222 |
+
# Kategoriye göre içerik üretimi
|
| 223 |
+
content_for_download = ""
|
| 224 |
+
|
| 225 |
+
if "Blog" in selected_prompt_category:
|
| 226 |
+
with st.spinner(f"🤖 {selected_service} ile Türkçe blog yazısı oluşturuluyor..."):
|
| 227 |
+
user_message = f"Konu: {keywords}. Yaklaşık {num_words} kelime uzunluğunda, '{blog_title}' başlıklı Türkçe blog yazısı yaz. Yanıtını tamamen Türkçe ver."
|
| 228 |
+
blog_content = make_ai_request(final_prompt, user_message, 4096)
|
| 229 |
+
|
| 230 |
+
if blog_content:
|
| 231 |
+
st.subheader("📝 Oluşturulan Blog Yazısı")
|
| 232 |
+
st.write(blog_content)
|
| 233 |
+
content_for_download = blog_content
|
| 234 |
+
else:
|
| 235 |
+
content_for_download = f"Blog yazısı: {keywords} konusunda içerik oluşturulamadı."
|
| 236 |
+
|
| 237 |
+
elif "LinkedIn" in selected_prompt_category:
|
| 238 |
+
with st.spinner(f"🤖 {selected_service} ile Türkçe LinkedIn postu oluşturuluyor..."):
|
| 239 |
+
user_message = f"Bu konular hakkında profesyonel bir Türkçe LinkedIn postu yaz: {keywords}. Yanıtını tamamen Türkçe ver."
|
| 240 |
+
linkedin_content = make_ai_request(final_prompt, user_message, 600)
|
| 241 |
+
|
| 242 |
+
if linkedin_content:
|
| 243 |
+
st.subheader("📝 Oluşturulan LinkedIn Postu")
|
| 244 |
+
st.write(linkedin_content)
|
| 245 |
+
content_for_download = linkedin_content
|
| 246 |
+
else:
|
| 247 |
+
content_for_download = f"LinkedIn Post: {keywords} hakkında profesyonel bir paylaşım oluşturulamadı."
|
| 248 |
+
|
| 249 |
+
elif "Tweet" in selected_prompt_category:
|
| 250 |
+
with st.spinner(f"🤖 {selected_service} ile Türkçe tweet oluşturuluyor..."):
|
| 251 |
+
user_message = f"Bu konular hakkında Türkçe tweet yaz: {keywords}. Yanıtını tamamen Türkçe ver."
|
| 252 |
+
tweet_content = make_ai_request(final_prompt, user_message, 100)
|
| 253 |
+
|
| 254 |
+
if tweet_content:
|
| 255 |
+
st.subheader("📝 Oluşturulan Tweet")
|
| 256 |
+
st.write(tweet_content)
|
| 257 |
+
content_for_download = tweet_content
|
| 258 |
+
else:
|
| 259 |
+
content_for_download = f"Tweet: {keywords} hakkında kısa bir paylaşım oluşturulamadı."
|
| 260 |
+
|
| 261 |
+
else:
|
| 262 |
+
with st.spinner(f"🤖 {selected_service} ile Türkçe {selected_prompt_category} oluşturuluyor..."):
|
| 263 |
+
user_message = f"Bu konular hakkında Türkçe içerik yaz: {keywords}. Yanıtını tamamen Türkçe ver."
|
| 264 |
+
general_content = make_ai_request(final_prompt, user_message, 2048)
|
| 265 |
+
|
| 266 |
+
if general_content:
|
| 267 |
+
st.subheader(f"📝 Oluşturulan {selected_prompt_category}")
|
| 268 |
+
st.write(general_content)
|
| 269 |
+
content_for_download = general_content
|
| 270 |
+
else:
|
| 271 |
+
content_for_download = f"{selected_prompt_category}: {keywords} konusunda içerik oluşturulamadı."
|
| 272 |
+
|
| 273 |
+
# DOCX İndirme
|
| 274 |
+
full_content = f"""
|
| 275 |
+
{selected_prompt_category} İçeriği
|
| 276 |
+
Başlık: {blog_title}
|
| 277 |
+
Anahtar Kelimeler: {keywords}
|
| 278 |
+
Kullanılan AI Servisi: {selected_service}
|
| 279 |
+
Kullanılan Prompt: {final_prompt}
|
| 280 |
+
|
| 281 |
+
--- İÇERİK ---
|
| 282 |
+
{content_for_download}
|
| 283 |
+
"""
|
| 284 |
+
|
| 285 |
+
def generate_docx(content):
|
| 286 |
+
buffer = BytesIO()
|
| 287 |
+
doc = Document()
|
| 288 |
+
doc.add_paragraph(content)
|
| 289 |
+
doc.save(buffer)
|
| 290 |
+
buffer.seek(0)
|
| 291 |
+
return buffer
|
| 292 |
+
|
| 293 |
+
docx_file = generate_docx(full_content)
|
| 294 |
+
|
| 295 |
+
st.download_button(
|
| 296 |
+
label="📄 Download as DOCX",
|
| 297 |
+
data=docx_file,
|
| 298 |
+
file_name=f"{selected_prompt_category.replace('🔗', '').replace('📝', '').replace('🐦', '').replace('🛍', '').strip()}_output.docx",
|
| 299 |
+
mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
elif submit_button and not keywords:
|
| 303 |
+
st.warning("⚠️ Lütfen keywords girin!")
|
| 304 |
+
|
| 305 |
+
else:
|
| 306 |
+
st.warning("⚠️ Lütfen giriş yapın veya kayıt olun.")
|