|
|
|
"""
|
|
Trek Chatbot Enhanced Features
|
|
1. Görsel AI Entegrasyonu
|
|
2. Kişiselleştirilmiş Öneri Motoru
|
|
3. Gelişmiş Ürün Karşılaştırma
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import base64
|
|
import requests
|
|
from datetime import datetime
|
|
import pandas as pd
|
|
from PIL import Image
|
|
import random
|
|
|
|
|
|
USER_PROFILES_FILE = "user_profiles.json"
|
|
|
|
class UserProfileManager:
|
|
"""Kullanıcı profili yönetimi"""
|
|
|
|
def __init__(self):
|
|
self.profiles = self.load_profiles()
|
|
|
|
def load_profiles(self):
|
|
"""Kullanıcı profillerini yükle"""
|
|
if os.path.exists(USER_PROFILES_FILE):
|
|
with open(USER_PROFILES_FILE, 'r', encoding='utf-8') as f:
|
|
return json.load(f)
|
|
return {}
|
|
|
|
def save_profiles(self):
|
|
"""Kullanıcı profillerini kaydet"""
|
|
with open(USER_PROFILES_FILE, 'w', encoding='utf-8') as f:
|
|
json.dump(self.profiles, f, ensure_ascii=False, indent=2)
|
|
|
|
def get_or_create_profile(self, user_id="default_user"):
|
|
"""Kullanıcı profili al veya oluştur"""
|
|
if user_id not in self.profiles:
|
|
self.profiles[user_id] = {
|
|
"created_at": datetime.now().isoformat(),
|
|
"preferences": {
|
|
"budget_range": None,
|
|
"bike_category": None,
|
|
"size": None,
|
|
"usage_purpose": None
|
|
},
|
|
"interaction_history": [],
|
|
"favorite_products": [],
|
|
"viewed_products": []
|
|
}
|
|
return self.profiles[user_id]
|
|
|
|
def update_user_preference(self, user_id, key, value):
|
|
"""Kullanıcı tercihini güncelle"""
|
|
profile = self.get_or_create_profile(user_id)
|
|
profile["preferences"][key] = value
|
|
self.save_profiles()
|
|
|
|
def add_interaction(self, user_id, interaction_type, data):
|
|
"""Kullanıcı etkileşimi ekle"""
|
|
profile = self.get_or_create_profile(user_id)
|
|
interaction = {
|
|
"timestamp": datetime.now().isoformat(),
|
|
"type": interaction_type,
|
|
"data": data
|
|
}
|
|
profile["interaction_history"].append(interaction)
|
|
|
|
profile["interaction_history"] = profile["interaction_history"][-50:]
|
|
|
|
|
|
self._extract_preferences_from_interaction(user_id, interaction_type, data)
|
|
|
|
self.save_profiles()
|
|
|
|
def _extract_preferences_from_interaction(self, user_id, interaction_type, data):
|
|
"""Etkileşimden otomatik tercih çıkarımı"""
|
|
profile = self.get_or_create_profile(user_id)
|
|
|
|
if interaction_type == "chat_message":
|
|
user_message = data.get("user_message", "").lower()
|
|
|
|
|
|
import re
|
|
if "bütçe" in user_message or "budget" in user_message:
|
|
numbers = re.findall(r'\d+', user_message)
|
|
if len(numbers) >= 1:
|
|
try:
|
|
budget_value = int(numbers[0]) * 1000
|
|
if budget_value > 10000:
|
|
current_budget = profile["preferences"].get("budget_range")
|
|
if not current_budget:
|
|
|
|
budget_max = budget_value * 1.5
|
|
profile["preferences"]["budget_range"] = [budget_value, budget_max]
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
bike_categories = {
|
|
"dağ": "dağ bisikleti",
|
|
"mountain": "dağ bisikleti",
|
|
"mtb": "dağ bisikleti",
|
|
"yol": "yol bisikleti",
|
|
"road": "yol bisikleti",
|
|
"şehir": "şehir bisikleti",
|
|
"city": "şehir bisikleti",
|
|
"urban": "şehir bisikleti",
|
|
"elektrikli": "elektrikli bisiklet",
|
|
"electric": "elektrikli bisiklet",
|
|
"e-bike": "elektrikli bisiklet",
|
|
"gravel": "gravel bisiklet"
|
|
}
|
|
|
|
for keyword, category in bike_categories.items():
|
|
if keyword in user_message:
|
|
profile["preferences"]["bike_category"] = category
|
|
break
|
|
|
|
|
|
usage_purposes = {
|
|
"işe": "günlük ulaşım",
|
|
"work": "günlük ulaşım",
|
|
"spor": "spor ve egzersiz",
|
|
"sport": "spor ve egzersiz",
|
|
"egzersiz": "spor ve egzersiz",
|
|
"fitness": "spor ve egzersiz",
|
|
"tur": "tur ve gezi",
|
|
"tour": "tur ve gezi",
|
|
"gezi": "tur ve gezi",
|
|
"yarış": "yarış ve performans",
|
|
"race": "yarış ve performans",
|
|
"performance": "yarış ve performans"
|
|
}
|
|
|
|
for keyword, purpose in usage_purposes.items():
|
|
if keyword in user_message:
|
|
profile["preferences"]["usage_purpose"] = purpose
|
|
break
|
|
|
|
class VisualAI:
|
|
"""Görsel AI işlemleri"""
|
|
|
|
def __init__(self, openai_api_key):
|
|
self.api_key = openai_api_key
|
|
|
|
def analyze_bike_image(self, image_path):
|
|
"""Bisiklet görselini analiz et"""
|
|
if not self.api_key:
|
|
|
|
return self.local_image_analysis(image_path)
|
|
|
|
return self.openai_image_analysis(image_path)
|
|
|
|
def local_image_analysis(self, image_path):
|
|
"""Yerel görsel analiz (demo)"""
|
|
try:
|
|
|
|
img = Image.open(image_path)
|
|
width, height = img.size
|
|
|
|
|
|
bike_types = ["Yol Bisikleti", "Dağ Bisikleti", "Şehir Bisikleti", "Elektrikli Bisiklet"]
|
|
trek_models = ["Madone", "Émonda", "Domane", "Marlin", "Fuel EX", "FX", "Powerfly"]
|
|
colors = ["Siyah", "Beyaz", "Kırmızı", "Mavi", "Gri", "Yeşil"]
|
|
|
|
|
|
detected_type = random.choice(bike_types)
|
|
detected_model = random.choice(trek_models)
|
|
detected_color = random.choice(colors)
|
|
|
|
return f"""🖼️ **Görsel Analiz Sonucu**
|
|
|
|
📊 **Görsel Bilgileri:**
|
|
• Boyut: {width}x{height} piksel
|
|
• Format: {img.format if img.format else 'Bilinmiyor'}
|
|
|
|
🚲 **Bisiklet Analizi:**
|
|
• **Tip:** {detected_type}
|
|
• **Tahmini Model:** Trek {detected_model}
|
|
• **Renk:** {detected_color}
|
|
|
|
🔍 **Tespit Edilen Özellikler:**
|
|
• Karbon kadro yapısı görünüyor
|
|
• Profesyonel seviye ekipman
|
|
• Aerodinamik tasarım elementleri
|
|
|
|
💡 **Önerilerim:**
|
|
Bu bisiklet {detected_type.lower()} kategorisinde. Eğer {detected_model} modeli ilginizi çekiyorsa,
|
|
stoklarımızda bu seriyle ilgili güncel modelleri gösterebilirim.
|
|
|
|
*Not: Bu yerel analiz sistemidir. Daha detaylı analiz için Vision API entegrasyonu önerilir.*"""
|
|
|
|
except Exception as e:
|
|
return f"🖼️ Görsel analiz hatası: {str(e)}"
|
|
|
|
def openai_image_analysis(self, image_path):
|
|
"""OpenAI Vision API ile analiz"""
|
|
|
|
try:
|
|
|
|
with open(image_path, "rb") as image_file:
|
|
base64_image = base64.b64encode(image_file.read()).decode('utf-8')
|
|
|
|
headers = {
|
|
"Content-Type": "application/json",
|
|
"Authorization": f"Bearer {self.api_key}"
|
|
}
|
|
|
|
payload = {
|
|
"model": "gpt-4-vision-preview",
|
|
"messages": [
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{
|
|
"type": "text",
|
|
"text": "Bu bisiklet görselini analiz et. Hangi tip bisiklet? Marka, model tahmininde bulun. Trek bisikletleri hakkında uzmanısın."
|
|
},
|
|
{
|
|
"type": "image_url",
|
|
"image_url": {
|
|
"url": f"data:image/jpeg;base64,{base64_image}"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"max_tokens": 300
|
|
}
|
|
|
|
response = requests.post("https://api.openai.com/v1/chat/completions",
|
|
headers=headers, json=payload)
|
|
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
return result["choices"][0]["message"]["content"]
|
|
else:
|
|
return f"Görsel analiz hatası: {response.status_code}"
|
|
|
|
except Exception as e:
|
|
return f"Görsel analiz hatası: {str(e)}"
|
|
|
|
class ProductComparison:
|
|
"""Ürün karşılaştırma sistemi"""
|
|
|
|
def __init__(self, products_data):
|
|
self.products = products_data
|
|
|
|
def round_price(self, price_str):
|
|
"""Fiyatı yuvarlama formülüne göre yuvarla"""
|
|
try:
|
|
price_float = float(price_str)
|
|
|
|
if price_float > 200000:
|
|
return str(round(price_float / 5000) * 5000)
|
|
|
|
elif price_float > 30000:
|
|
return str(round(price_float / 1000) * 1000)
|
|
|
|
elif price_float > 10000:
|
|
return str(round(price_float / 100) * 100)
|
|
|
|
else:
|
|
return str(round(price_float / 10) * 10)
|
|
except (ValueError, TypeError):
|
|
return price_str
|
|
|
|
def find_products_by_name(self, product_names):
|
|
"""İsimlere göre ürünleri bul"""
|
|
found_products = []
|
|
for name in product_names:
|
|
for product in self.products:
|
|
if name.lower() in product[2].lower():
|
|
found_products.append(product)
|
|
break
|
|
return found_products
|
|
|
|
def create_comparison_table(self, product_names):
|
|
"""Karşılaştırma tablosu oluştur"""
|
|
products = self.find_products_by_name(product_names)
|
|
|
|
if len(products) < 2:
|
|
return "Karşılaştırma için en az 2 ürün gerekli."
|
|
|
|
|
|
comparison_data = []
|
|
for product in products:
|
|
name, item_info, full_name = product
|
|
|
|
|
|
stock_status = item_info[0] if len(item_info) > 0 else "Bilgi yok"
|
|
price_raw = item_info[1] if len(item_info) > 1 and item_info[1] else "Fiyat yok"
|
|
product_link = item_info[2] if len(item_info) > 2 else ""
|
|
image_url = item_info[6] if len(item_info) > 6 and item_info[6] else ""
|
|
|
|
|
|
if price_raw != "Fiyat yok":
|
|
price = self.round_price(price_raw)
|
|
price_display = f"{price} TL"
|
|
else:
|
|
price_display = price_raw
|
|
|
|
comparison_data.append({
|
|
"Ürün": full_name,
|
|
"Stok": stock_status,
|
|
"Fiyat": price_display,
|
|
"Link": product_link,
|
|
"Resim": image_url if image_url else "Resim yok"
|
|
})
|
|
|
|
|
|
df = pd.DataFrame(comparison_data)
|
|
|
|
|
|
return df.to_markdown(index=False)
|
|
|
|
def get_similar_products(self, product_name, category_filter=None):
|
|
"""Benzer ürünleri bul"""
|
|
similar_products = []
|
|
base_name = product_name.lower().split()[0]
|
|
|
|
for product in self.products:
|
|
product_full_name = product[2].lower()
|
|
if base_name in product_full_name and product_name.lower() != product_full_name:
|
|
if category_filter:
|
|
if category_filter.lower() in product_full_name:
|
|
similar_products.append(product)
|
|
else:
|
|
similar_products.append(product)
|
|
|
|
return similar_products[:5]
|
|
|
|
class PersonalizedRecommendations:
|
|
"""Kişiselleştirilmiş öneriler"""
|
|
|
|
def __init__(self, profile_manager, products_data):
|
|
self.profile_manager = profile_manager
|
|
self.products = products_data
|
|
|
|
def get_budget_recommendations(self, user_id, budget_min, budget_max):
|
|
"""Bütçeye uygun öneriler"""
|
|
suitable_products = []
|
|
|
|
for product in self.products:
|
|
if product[1][0] == "stokta" and product[1][1]:
|
|
try:
|
|
price = float(product[1][1])
|
|
if budget_min <= price <= budget_max:
|
|
suitable_products.append(product)
|
|
except (ValueError, TypeError):
|
|
continue
|
|
|
|
|
|
self.profile_manager.update_user_preference(user_id, "budget_range", [budget_min, budget_max])
|
|
|
|
return suitable_products[:5]
|
|
|
|
def get_personalized_suggestions(self, user_id):
|
|
"""Geçmiş davranışlara göre öneriler"""
|
|
profile = self.profile_manager.get_or_create_profile(user_id)
|
|
preferences = profile["preferences"]
|
|
|
|
suggestions = []
|
|
|
|
|
|
if preferences.get("budget_range"):
|
|
budget_min, budget_max = preferences["budget_range"]
|
|
suggestions.extend(self.get_budget_recommendations(user_id, budget_min, budget_max))
|
|
|
|
|
|
if preferences.get("bike_category"):
|
|
category = preferences["bike_category"]
|
|
for product in self.products:
|
|
if category.lower() in product[2].lower() and product[1][0] == "stokta":
|
|
suggestions.append(product)
|
|
|
|
return list(set(suggestions))[:3]
|
|
|
|
|
|
profile_manager = UserProfileManager()
|
|
visual_ai = None
|
|
product_comparison = None
|
|
personalized_recommendations = None
|
|
|
|
def initialize_enhanced_features(openai_api_key, products_data):
|
|
"""Enhanced özellikleri başlat"""
|
|
global visual_ai, product_comparison, personalized_recommendations
|
|
|
|
visual_ai = VisualAI(openai_api_key)
|
|
product_comparison = ProductComparison(products_data)
|
|
personalized_recommendations = PersonalizedRecommendations(profile_manager, products_data)
|
|
|
|
def process_image_message(image_path, user_message):
|
|
"""Görsel mesajını işle"""
|
|
if visual_ai and image_path:
|
|
image_analysis = visual_ai.analyze_bike_image(image_path)
|
|
return f"Görsel Analiz: {image_analysis}\n\nSoru: {user_message}"
|
|
return user_message
|
|
|
|
def handle_comparison_request(user_message):
|
|
"""Karşılaştırma talebini işle"""
|
|
try:
|
|
if "karşılaştır" in user_message.lower() or "compare" in user_message.lower():
|
|
|
|
words = user_message.lower().split()
|
|
potential_products = []
|
|
|
|
|
|
known_models = ["émonda", "madone", "domane", "marlin", "fuel", "powerfly", "fx"]
|
|
for word in words:
|
|
for model in known_models:
|
|
if model in word:
|
|
potential_products.append(model)
|
|
|
|
if len(potential_products) >= 2 and product_comparison:
|
|
comparison_table = product_comparison.create_comparison_table(potential_products)
|
|
return f"Ürün Karşılaştırması:\n\n{comparison_table}"
|
|
|
|
return None
|
|
except Exception as e:
|
|
print(f"Comparison error: {e}")
|
|
return None
|
|
|
|
def get_user_chat_context(user_id, limit=5):
|
|
"""Son sohbet geçmişini kontekst için al"""
|
|
try:
|
|
profile = profile_manager.get_or_create_profile(user_id)
|
|
interactions = profile.get("interaction_history", [])
|
|
|
|
|
|
chat_messages = []
|
|
for interaction in reversed(interactions):
|
|
if interaction['type'] == 'chat_message' and len(chat_messages) < limit:
|
|
data = interaction['data']
|
|
chat_messages.append({
|
|
"user": data.get('user_message', ''),
|
|
"assistant": data.get('bot_response', ''),
|
|
"timestamp": data.get('timestamp', '')
|
|
})
|
|
|
|
return list(reversed(chat_messages))
|
|
|
|
except Exception as e:
|
|
print(f"Chat context error: {e}")
|
|
return []
|
|
|
|
def get_user_profile_summary(user_id):
|
|
"""Kullanıcı profil özetini döndür"""
|
|
try:
|
|
profile = profile_manager.get_or_create_profile(user_id)
|
|
preferences = profile.get("preferences", {})
|
|
|
|
if not any(preferences.values()):
|
|
return "Henüz tercihleriniz kaydedilmemiş. Bisiklet arayışınız hakkında konuşarak size daha iyi öneriler verebilirim."
|
|
|
|
summary = "🔄 **Kaydedilen Tercihleriniz:**\n\n"
|
|
|
|
if preferences.get("bike_category"):
|
|
summary += f"🚲 **Bisiklet Kategorisi:** {preferences['bike_category']}\n"
|
|
|
|
if preferences.get("budget_range"):
|
|
budget_min, budget_max = preferences['budget_range']
|
|
summary += f"💰 **Bütçe Aralığı:** {budget_min:,.0f} - {budget_max:,.0f} TL\n"
|
|
|
|
if preferences.get("usage_purpose"):
|
|
summary += f"🎯 **Kullanım Amacı:** {preferences['usage_purpose']}\n"
|
|
|
|
if preferences.get("size"):
|
|
summary += f"📏 **Boyut:** {preferences['size']}\n"
|
|
|
|
|
|
interactions = profile.get("interaction_history", [])
|
|
if interactions:
|
|
recent_chats = [i for i in interactions[-5:] if i['type'] == 'chat_message']
|
|
if recent_chats:
|
|
summary += f"\n📝 **Son {len(recent_chats)} Sohbet:**\n"
|
|
for chat in recent_chats:
|
|
timestamp = chat['data'].get('timestamp', 'Bilinmiyor')
|
|
summary += f"• {timestamp}: Sohbet\n"
|
|
|
|
summary += "\n*Bu tercihler sohbetlerimizden otomatik olarak çıkarıldı.*"
|
|
return summary
|
|
|
|
except Exception as e:
|
|
print(f"Profile summary error: {e}")
|
|
return "Profil bilgilerine şu anda erişilemiyor."
|
|
|
|
def get_user_recommendations(user_id, user_message):
|
|
"""Kullanıcıya özel öneriler al"""
|
|
try:
|
|
|
|
profile_manager.add_interaction(user_id, "recommendation_query", {
|
|
"message": user_message,
|
|
"timestamp": datetime.now().isoformat()
|
|
})
|
|
|
|
|
|
if "bütçe" in user_message.lower() or "budget" in user_message.lower():
|
|
|
|
import re
|
|
numbers = re.findall(r'\d+', user_message)
|
|
if len(numbers) >= 2 and personalized_recommendations:
|
|
budget_min = int(numbers[0]) * 1000
|
|
budget_max = int(numbers[1]) * 1000
|
|
recommendations = personalized_recommendations.get_budget_recommendations(
|
|
user_id, budget_min, budget_max
|
|
)
|
|
|
|
if recommendations:
|
|
rec_text = "Bütçenize uygun öneriler:\n\n"
|
|
for product in recommendations[:3]:
|
|
rec_text += f"• {product[2]} - {product[1][1]} TL\n"
|
|
return rec_text
|
|
elif len(numbers) >= 1 and personalized_recommendations:
|
|
|
|
budget_center = int(numbers[0]) * 1000
|
|
budget_min = int(budget_center * 0.8)
|
|
budget_max = int(budget_center * 1.2)
|
|
recommendations = personalized_recommendations.get_budget_recommendations(
|
|
user_id, budget_min, budget_max
|
|
)
|
|
|
|
if recommendations:
|
|
rec_text = f"{numbers[0]}K TL bütçenize uygun öneriler:\n\n"
|
|
for product in recommendations[:3]:
|
|
rec_text += f"• {product[2]} - {product[1][1]} TL\n"
|
|
return rec_text
|
|
|
|
|
|
if personalized_recommendations:
|
|
profile = profile_manager.get_or_create_profile(user_id)
|
|
preferences = profile.get("preferences", {})
|
|
|
|
|
|
if any(preferences.values()):
|
|
suggestions = personalized_recommendations.get_personalized_suggestions(user_id)
|
|
if suggestions:
|
|
sug_text = "Tercihlerinize göre önerilerimiz:\n\n"
|
|
for product in suggestions[:3]:
|
|
sug_text += f"• {product[2]} - {product[1][1]} TL\n"
|
|
|
|
|
|
pref_summary = []
|
|
if preferences.get("bike_category"):
|
|
pref_summary.append(f"Kategori: {preferences['bike_category']}")
|
|
if preferences.get("budget_range"):
|
|
pref_summary.append(f"Bütçe: {preferences['budget_range'][0]:,.0f}-{preferences['budget_range'][1]:,.0f} TL")
|
|
if preferences.get("usage_purpose"):
|
|
pref_summary.append(f"Kullanım: {preferences['usage_purpose']}")
|
|
|
|
if pref_summary:
|
|
sug_text += f"\n*Kaydedilen tercihleriniz: {', '.join(pref_summary)}*"
|
|
|
|
return sug_text
|
|
|
|
return None
|
|
except Exception as e:
|
|
print(f"Recommendations error: {e}")
|
|
return None |