|
|
| """
|
| 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 |