| |
| """ |
| WhatsApp Pasif Profil Sistemi |
| Kullanıcıya soru sormadan sohbet analizi ile profil oluşturur |
| """ |
|
|
| import re |
| import json |
| import os |
| from datetime import datetime, timedelta |
| from typing import Dict, List, Optional, Any |
|
|
| class WhatsAppPassiveProfiler: |
| """Sohbet analizi ile kullanıcı profili çıkarır""" |
| |
| def __init__(self): |
| self.profiles_file = "user_profiles.json" |
| self.profiles = self.load_profiles() |
| |
| |
| self.budget_patterns = [ |
| r'bütçem?\s*(\d+)[\s-]*(\d+)?\s*k?\s*(bin|bin tl|tl)?', |
| r'(\d+)[\s-]*(\d+)?\s*k?\s*(bin|bin tl|tl)?\s*bütçe', |
| r'(\d+)[\s-]*(\d+)?\s*k?\s*(bin|bin tl|tl)?\s*arasında', |
| r'maksimum\s*(\d+)\s*k?\s*(bin|bin tl|tl)?', |
| r'en fazla\s*(\d+)\s*k?\s*(bin|bin tl|tl)?' |
| ] |
| |
| |
| self.category_keywords = { |
| "dağ_bisikleti": ["dağ", "dag", "offroad", "patika", "doğa", "orman", "marlin", "fuel", "procaliber"], |
| "yol_bisikleti": ["yol", "asfalt", "hız", "yarış", "triathlon", "émonda", "madone", "domane"], |
| "şehir_bisikleti": ["şehir", "kent", "günlük", "işe gidip gelme", "fx", "ds", "verve"], |
| "elektrikli": ["elektrikli", "electric", "e-bike", "ebike", "batarya", "powerfly", "rail"], |
| "gravel": ["gravel", "çakıl", "macera", "touring", "checkpoint"] |
| } |
| |
| |
| self.usage_patterns = { |
| "spor": ["spor", "antrenman", "kondisyon", "fitness", "egzersiz", "yarış"], |
| "günlük": ["işe gitmek", "günlük", "şehir içi", "ulaşım", "market", "alışveriş"], |
| "hobi": ["hobi", "eğlence", "gezinti", "keyif", "hafta sonu", "macera"], |
| "profesyonel": ["profesyonel", "yarış", "müsabaka", "antrenör", "ciddi"] |
| } |
| |
| |
| self.size_patterns = { |
| "boy": r'boyum\s*(\d+)\s*cm?|(\d+)\s*cm?\s*boy', |
| "kilo": r'kilom\s*(\d+)\s*kg?|(\d+)\s*kg?\s*kilo', |
| "beden": r'beden[im]?\s*([xsl]+)|([xsl]+)\s*beden' |
| } |
| |
| def load_profiles(self) -> Dict: |
| """Mevcut profilleri yükle""" |
| if os.path.exists(self.profiles_file): |
| try: |
| with open(self.profiles_file, 'r', encoding='utf-8') as f: |
| return json.load(f) |
| except Exception as e: |
| print(f"Profil yükleme hatası: {e}") |
| return {} |
| |
| def save_profiles(self): |
| """Profilleri kaydet""" |
| try: |
| with open(self.profiles_file, 'w', encoding='utf-8') as f: |
| json.dump(self.profiles, f, ensure_ascii=False, indent=2, default=str) |
| except Exception as e: |
| print(f"Profil kaydetme hatası: {e}") |
| |
| def get_or_create_profile(self, phone_number: str) -> Dict: |
| """Profil getir veya oluştur""" |
| if phone_number not in self.profiles: |
| self.profiles[phone_number] = { |
| "created_at": datetime.now(), |
| "last_updated": datetime.now(), |
| "total_messages": 0, |
| "preferences": { |
| "budget_min": None, |
| "budget_max": None, |
| "categories": [], |
| "usage_purpose": [], |
| "size_info": {}, |
| "brand_preferences": [], |
| "color_preferences": [] |
| }, |
| "behavior": { |
| "price_sensitive": False, |
| "tech_interested": False, |
| "comparison_lover": False, |
| "quick_decider": False, |
| "research_oriented": False |
| }, |
| "interests": { |
| "viewed_products": [], |
| "compared_products": [], |
| "favorite_features": [], |
| "mentioned_brands": [] |
| }, |
| "statistics": { |
| "comparison_requests": 0, |
| "budget_queries": 0, |
| "technical_questions": 0, |
| "price_questions": 0, |
| "availability_questions": 0 |
| } |
| } |
| return self.profiles[phone_number] |
| |
| def analyze_message(self, phone_number: str, message: str) -> Dict: |
| """Mesajı analiz et ve profili güncelle""" |
| profile = self.get_or_create_profile(phone_number) |
| message_lower = message.lower() |
| |
| |
| profile["total_messages"] += 1 |
| profile["last_updated"] = datetime.now() |
| |
| analysis_results = { |
| "budget_detected": False, |
| "category_detected": False, |
| "usage_detected": False, |
| "size_detected": False, |
| "behavior_indicators": [] |
| } |
| |
| |
| budget_info = self.extract_budget(message_lower) |
| if budget_info: |
| profile["preferences"]["budget_min"] = budget_info["min"] |
| profile["preferences"]["budget_max"] = budget_info["max"] |
| profile["statistics"]["budget_queries"] += 1 |
| analysis_results["budget_detected"] = True |
| |
| |
| detected_categories = self.detect_categories(message_lower) |
| if detected_categories: |
| for category in detected_categories: |
| if category not in profile["preferences"]["categories"]: |
| profile["preferences"]["categories"].append(category) |
| analysis_results["category_detected"] = True |
| |
| |
| usage_purposes = self.detect_usage_purpose(message_lower) |
| if usage_purposes: |
| for purpose in usage_purposes: |
| if purpose not in profile["preferences"]["usage_purpose"]: |
| profile["preferences"]["usage_purpose"].append(purpose) |
| analysis_results["usage_detected"] = True |
| |
| |
| size_info = self.extract_size_info(message_lower) |
| if size_info: |
| profile["preferences"]["size_info"].update(size_info) |
| analysis_results["size_detected"] = True |
| |
| |
| behavior_indicators = self.analyze_behavior(message_lower) |
| for behavior, detected in behavior_indicators.items(): |
| if detected: |
| profile["behavior"][behavior] = True |
| analysis_results["behavior_indicators"].append(behavior) |
| |
| |
| self.update_statistics(profile, message_lower) |
| |
| |
| self.save_profiles() |
| |
| return analysis_results |
| |
| def extract_budget(self, message: str) -> Optional[Dict]: |
| """Bütçe bilgisini çıkar""" |
| for pattern in self.budget_patterns: |
| match = re.search(pattern, message) |
| if match: |
| numbers = [g for g in match.groups() if g and g.isdigit()] |
| if numbers: |
| if len(numbers) == 1: |
| |
| budget = int(numbers[0]) |
| if budget < 1000: |
| budget *= 1000 |
| return {"min": int(budget * 0.7), "max": budget} |
| elif len(numbers) >= 2: |
| |
| min_budget = int(numbers[0]) |
| max_budget = int(numbers[1]) |
| if min_budget < 1000: |
| min_budget *= 1000 |
| if max_budget < 1000: |
| max_budget *= 1000 |
| return {"min": min_budget, "max": max_budget} |
| return None |
| |
| def detect_categories(self, message: str) -> List[str]: |
| """Kategori tercihlerini tespit et""" |
| detected = [] |
| for category, keywords in self.category_keywords.items(): |
| if any(keyword in message for keyword in keywords): |
| detected.append(category) |
| return detected |
| |
| def detect_usage_purpose(self, message: str) -> List[str]: |
| """Kullanım amacını tespit et""" |
| detected = [] |
| for purpose, keywords in self.usage_patterns.items(): |
| if any(keyword in message for keyword in keywords): |
| detected.append(purpose) |
| return detected |
| |
| def extract_size_info(self, message: str) -> Dict: |
| """Boy/beden bilgisini çıkar""" |
| size_info = {} |
| |
| |
| boy_match = re.search(self.size_patterns["boy"], message) |
| if boy_match: |
| boy = boy_match.group(1) or boy_match.group(2) |
| if boy: |
| size_info["height"] = int(boy) |
| |
| |
| kilo_match = re.search(self.size_patterns["kilo"], message) |
| if kilo_match: |
| kilo = kilo_match.group(1) or kilo_match.group(2) |
| if kilo: |
| size_info["weight"] = int(kilo) |
| |
| |
| beden_match = re.search(self.size_patterns["beden"], message) |
| if beden_match: |
| beden = beden_match.group(1) or beden_match.group(2) |
| if beden: |
| size_info["size"] = beden.upper() |
| |
| return size_info |
| |
| def analyze_behavior(self, message: str) -> Dict[str, bool]: |
| """Davranış kalıplarını analiz et""" |
| behavior = {} |
| |
| |
| price_keywords = ["ucuz", "fiyat", "indirim", "kampanya", "ekonomik", "bütçe"] |
| behavior["price_sensitive"] = any(keyword in message for keyword in price_keywords) |
| |
| |
| tech_keywords = ["teknik", "özellik", "ağırlık", "malzeme", "karbon", "alüminyum", "vites"] |
| behavior["tech_interested"] = any(keyword in message for keyword in tech_keywords) |
| |
| |
| comparison_keywords = ["karşılaştır", "fark", "hangisi", "arası", "seçim"] |
| behavior["comparison_lover"] = any(keyword in message for keyword in comparison_keywords) |
| |
| |
| research_keywords = ["detay", "bilgi", "araştır", "inceleme", "test", "deneyim"] |
| behavior["research_oriented"] = any(keyword in message for keyword in research_keywords) |
| |
| return behavior |
| |
| def update_statistics(self, profile: Dict, message: str): |
| """İstatistikleri güncelle""" |
| if "karşılaştır" in message: |
| profile["statistics"]["comparison_requests"] += 1 |
| |
| if any(word in message for word in ["fiyat", "kaç para", "ne kadar"]): |
| profile["statistics"]["price_questions"] += 1 |
| |
| if any(word in message for word in ["stok", "var mı", "mevcut"]): |
| profile["statistics"]["availability_questions"] += 1 |
| |
| if any(word in message for word in ["teknik", "özellik", "detay"]): |
| profile["statistics"]["technical_questions"] += 1 |
| |
| def get_profile_summary(self, phone_number: str) -> Dict: |
| """Profil özetini döndür""" |
| if phone_number not in self.profiles: |
| return {"exists": False} |
| |
| profile = self.profiles[phone_number] |
| |
| |
| confidence = min(profile["total_messages"] / 10.0, 1.0) |
| |
| summary = { |
| "exists": True, |
| "confidence": confidence, |
| "total_messages": profile["total_messages"], |
| "preferences": profile["preferences"], |
| "behavior": profile["behavior"], |
| "top_categories": profile["preferences"]["categories"][:3], |
| "is_budget_defined": profile["preferences"]["budget_min"] is not None, |
| "is_tech_savvy": profile["behavior"]["tech_interested"], |
| "is_price_conscious": profile["behavior"]["price_sensitive"], |
| "interaction_style": self.get_interaction_style(profile) |
| } |
| |
| return summary |
| |
| def get_interaction_style(self, profile: Dict) -> str: |
| """Etkileşim stilini belirle""" |
| stats = profile["statistics"] |
| behavior = profile["behavior"] |
| |
| if stats["comparison_requests"] > 2 and behavior["research_oriented"]: |
| return "analytical" |
| elif behavior["price_sensitive"] and stats["budget_queries"] > 0: |
| return "budget_conscious" |
| elif behavior["tech_interested"] and stats["technical_questions"] > 1: |
| return "technical" |
| elif stats["comparison_requests"] == 0 and profile["total_messages"] < 5: |
| return "decisive" |
| else: |
| return "balanced" |
| |
| def get_personalized_suggestions(self, phone_number: str, products_data: List) -> Dict: |
| """Kişiselleştirilmiş öneriler""" |
| profile_summary = self.get_profile_summary(phone_number) |
| |
| if not profile_summary["exists"] or profile_summary["confidence"] < 0.3: |
| return {"personalized": False, "reason": "insufficient_data"} |
| |
| suggestions = { |
| "personalized": True, |
| "user_style": profile_summary["interaction_style"], |
| "budget_aware": profile_summary["is_budget_defined"], |
| "recommendations": [] |
| } |
| |
| |
| filtered_products = products_data |
| if profile_summary["preferences"]["budget_min"]: |
| budget_min = profile_summary["preferences"]["budget_min"] |
| budget_max = profile_summary["preferences"]["budget_max"] |
| filtered_products = [ |
| p for p in products_data |
| if p[1][0] == "stokta" and p[1][1] and |
| budget_min <= float(p[1][1]) <= budget_max |
| ] |
| |
| |
| if profile_summary["top_categories"]: |
| category_products = [] |
| for category in profile_summary["top_categories"]: |
| category_products.extend([ |
| p for p in filtered_products |
| if any(keyword in p[2].lower() for keyword in self.category_keywords.get(category, [])) |
| ]) |
| if category_products: |
| filtered_products = category_products |
| |
| suggestions["recommendations"] = filtered_products[:5] |
| return suggestions |
|
|
| |
| passive_profiler = WhatsAppPassiveProfiler() |
|
|
| def analyze_user_message(phone_number: str, message: str) -> Dict: |
| """Kullanıcı mesajını analiz et ve profili güncelle""" |
| return passive_profiler.analyze_message(phone_number, message) |
|
|
| def get_user_profile_summary(phone_number: str) -> Dict: |
| """Kullanıcı profil özetini getir""" |
| return passive_profiler.get_profile_summary(phone_number) |
|
|
| def get_personalized_recommendations(phone_number: str, products_data: List) -> Dict: |
| """Kişiselleştirilmiş öneriler getir""" |
| return passive_profiler.get_personalized_suggestions(phone_number, products_data) |