BF-WAB / customer_manager.py
SamiKoen's picture
Add intelligent name request system to CRM
d9aab9b
raw
history blame
18.7 kB
"""
CRM (Customer Relationship Management) System
Müşteri tanıma, segmentasyon ve kişiselleştirilmiş yanıtlar
"""
import json
import os
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple
import re
class CustomerManager:
def __init__(self, db_path: str = "customers.json"):
self.db_path = db_path
self.customers = self._load_database()
def _load_database(self) -> Dict:
"""Müşteri veritabanını yükle"""
if os.path.exists(self.db_path):
try:
with open(self.db_path, 'r', encoding='utf-8') as f:
return json.load(f)
except:
return {}
return {}
def _save_database(self):
"""Veritabanını kaydet"""
with open(self.db_path, 'w', encoding='utf-8') as f:
json.dump(self.customers, f, ensure_ascii=False, indent=2)
def _extract_name_from_message(self, message: str) -> Optional[str]:
"""Mesajdan isim çıkarmaya çalış"""
# "Ben Ahmet", "Benim adım Mehmet", "Merhaba ben Ali" gibi kalıplar
patterns = [
r"ben[im]?\s+ad[ıi]m?\s+(\w+)",
r"ben\s+(\w+)",
r"ad[ıi]m\s+(\w+)",
r"(\w+)\s+diye",
]
message_lower = message.lower()
for pattern in patterns:
match = re.search(pattern, message_lower)
if match:
name = match.group(1)
# İlk harfi büyük yap
return name.capitalize()
return None
def _detect_interests(self, message: str) -> List[str]:
"""Mesajdan ilgi alanlarını çıkar"""
interests = []
message_lower = message.lower()
# Bisiklet kategorileri
if any(word in message_lower for word in ["dağ", "mountain", "mtb"]):
interests.append("mountain_bike")
if any(word in message_lower for word in ["yol", "road", "yarış"]):
interests.append("road_bike")
if any(word in message_lower for word in ["şehir", "city", "urban"]):
interests.append("city_bike")
# Marka ve modeller
if "trek" in message_lower:
interests.append("trek")
if "marlin" in message_lower:
interests.append("marlin_series")
if "fx" in message_lower:
interests.append("fx_series")
if "domane" in message_lower:
interests.append("domane_series")
if "madone" in message_lower:
interests.append("madone_series")
# Özellikler
if any(word in message_lower for word in ["elektrik", "e-bike", "ebike"]):
interests.append("e_bike")
if any(word in message_lower for word in ["karbon", "carbon"]):
interests.append("carbon")
if any(word in message_lower for word in ["kadın", "women", "wsd"]):
interests.append("women_specific")
# Bütçe
if any(word in message_lower for word in ["ucuz", "uygun fiyat", "ekonomik"]):
interests.append("budget_friendly")
if any(word in message_lower for word in ["premium", "high-end", "üst segment"]):
interests.append("premium")
return interests
def _calculate_segment(self, customer: Dict) -> str:
"""Müşteri segmentini hesapla"""
total_purchases = len(customer.get("purchases", []))
total_spent = sum(p.get("price", 0) for p in customer.get("purchases", []))
total_queries = customer.get("total_queries", 0)
# Son aktivite kontrolü
last_interaction = datetime.fromisoformat(customer.get("last_interaction", datetime.now().isoformat()))
days_since_last = (datetime.now() - last_interaction).days
# VIP kriterler
if total_purchases >= 3 or total_spent >= 50000:
return "VIP"
# Potansiyel müşteri
if total_queries >= 5 and total_purchases == 0:
return "Potansiyel"
# Kayıp riski
if days_since_last > 30 and total_purchases > 0:
return "Kayıp Riski"
# Yeni müşteri (ilk 7 gün)
first_seen = datetime.fromisoformat(customer.get("first_seen", datetime.now().isoformat()))
if (datetime.now() - first_seen).days <= 7:
return "Yeni"
# Aktif müşteri
if days_since_last <= 7:
return "Aktif"
# Standart
return "Standart"
def get_or_create_customer(self, phone: str, name: Optional[str] = None) -> Dict:
"""Müşteriyi getir veya yeni oluştur"""
if phone in self.customers:
# Var olan müşteri
customer = self.customers[phone]
customer["last_interaction"] = datetime.now().isoformat()
# İsim güncellemesi
if name and not customer.get("name"):
customer["name"] = name
# Segment güncelle
customer["segment"] = self._calculate_segment(customer)
else:
# Yeni müşteri
customer = {
"phone": phone,
"name": name,
"first_seen": datetime.now().isoformat(),
"last_interaction": datetime.now().isoformat(),
"total_queries": 0,
"purchases": [],
"interests": [],
"searched_products": [],
"segment": "Yeni",
"notes": [],
"preferred_contact_time": None # "morning", "afternoon", "evening"
}
self.customers[phone] = customer
self._save_database()
return customer
def update_interaction(self, phone: str, message: str, product_searched: Optional[str] = None) -> Dict:
"""Müşteri etkileşimini güncelle"""
customer = self.get_or_create_customer(phone)
# Sorgu sayısını artır
customer["total_queries"] = customer.get("total_queries", 0) + 1
# İsim çıkarma denemesi
if not customer.get("name"):
extracted_name = self._extract_name_from_message(message)
if extracted_name:
customer["name"] = extracted_name
# İlgi alanlarını güncelle
new_interests = self._detect_interests(message)
existing_interests = set(customer.get("interests", []))
customer["interests"] = list(existing_interests.union(set(new_interests)))
# Aranan ürünü kaydet
if product_searched:
searched_products = customer.get("searched_products", [])
# Son 10 aramayı tut
searched_products.append({
"product": product_searched,
"date": datetime.now().isoformat()
})
customer["searched_products"] = searched_products[-10:]
# İletişim zamanı tercihi
hour = datetime.now().hour
if 6 <= hour < 12:
time_pref = "morning"
elif 12 <= hour < 18:
time_pref = "afternoon"
else:
time_pref = "evening"
# En çok hangi zaman diliminde yazıyor
if not customer.get("preferred_contact_time"):
customer["preferred_contact_time"] = time_pref
# Segment güncelle
customer["segment"] = self._calculate_segment(customer)
# Son etkileşim zamanı
customer["last_interaction"] = datetime.now().isoformat()
self._save_database()
return customer
def add_purchase(self, phone: str, product: str, price: float) -> Dict:
"""Satın alma kaydı ekle"""
customer = self.get_or_create_customer(phone)
purchase = {
"product": product,
"price": price,
"date": datetime.now().isoformat()
}
customer.setdefault("purchases", []).append(purchase)
customer["segment"] = self._calculate_segment(customer)
self._save_database()
return customer
def get_customer_context(self, phone: str) -> Dict:
"""Müşteri bağlamını getir"""
customer = self.customers.get(phone, {})
if not customer:
return {
"is_new": True,
"greeting": "Merhaba! Trek bisikletleri hakkında size nasıl yardımcı olabilirim?"
}
context = {
"is_new": False,
"name": customer.get("name"),
"segment": customer.get("segment", "Standart"),
"total_queries": customer.get("total_queries", 0),
"interests": customer.get("interests", []),
"last_product": None,
"days_since_last": 0,
"greeting": ""
}
# Son aranan ürün
searched_products = customer.get("searched_products", [])
if searched_products:
context["last_product"] = searched_products[-1]["product"]
last_search_date = datetime.fromisoformat(searched_products[-1]["date"])
context["days_since_last"] = (datetime.now() - last_search_date).days
# Kişiselleştirilmiş selamlama oluştur
context["greeting"] = self._generate_greeting(customer, context)
return context
def _generate_greeting(self, customer: Dict, context: Dict) -> str:
"""Kişiselleştirilmiş selamlama oluştur"""
segment = customer.get("segment", "Standart")
name = customer.get("name", "")
# Saat bazlı selamlama
hour = datetime.now().hour
if 6 <= hour < 12:
time_greeting = "Günaydın"
elif 12 <= hour < 18:
time_greeting = "İyi günler"
else:
time_greeting = "İyi akşamlar"
# Segment bazlı selamlama
if segment == "VIP":
if name:
greeting = f"{time_greeting} {name} Bey/Hanım! Değerli müşterimiz olarak size özel VIP avantajlarımız mevcut."
else:
greeting = f"{time_greeting}! Değerli müşterimize özel VIP avantajlarımız mevcut."
elif segment == "Yeni":
greeting = f"{time_greeting}! Trek bisikletleri dünyasına hoş geldiniz! Size nasıl yardımcı olabilirim?"
elif segment == "Potansiyel":
if context.get("last_product"):
greeting = f"{time_greeting}! Daha önce sorduğunuz {context['last_product']} veya başka bir konuda size yardımcı olabilir miyim?"
else:
greeting = f"{time_greeting}! Bisiklet arayışınızda size nasıl yardımcı olabilirim?"
elif segment == "Kayıp Riski":
if name:
greeting = f"{time_greeting} {name} Bey/Hanım! Sizi özledik! Size özel geri dönüş kampanyamız var."
else:
greeting = f"{time_greeting}! Sizi tekrar aramızda görmek güzel! Size özel fırsatlarımız var."
elif segment == "Aktif":
if name:
greeting = f"{time_greeting} {name} Bey/Hanım! Tekrar hoş geldiniz!"
else:
greeting = f"{time_greeting}! Tekrar hoş geldiniz!"
# Son aranan ürün varsa ekle
if context.get("last_product") and context.get("days_since_last", 99) <= 3:
greeting += f" {context['last_product']} hakkında bilgi almak ister misiniz?"
else: # Standart
if name:
greeting = f"{time_greeting} {name} Bey/Hanım! Size nasıl yardımcı olabilirim?"
else:
greeting = f"{time_greeting}! Size nasıl yardımcı olabilirim?"
return greeting
def get_analytics(self) -> Dict:
"""Analitik özet döndür"""
total_customers = len(self.customers)
segments = {}
total_purchases = 0
total_revenue = 0
active_today = 0
today = datetime.now().date()
for customer in self.customers.values():
# Segment dağılımı
segment = customer.get("segment", "Standart")
segments[segment] = segments.get(segment, 0) + 1
# Satış istatistikleri
purchases = customer.get("purchases", [])
total_purchases += len(purchases)
total_revenue += sum(p.get("price", 0) for p in purchases)
# Bugün aktif olanlar
last_interaction = datetime.fromisoformat(customer.get("last_interaction", "2020-01-01"))
if last_interaction.date() == today:
active_today += 1
return {
"total_customers": total_customers,
"segments": segments,
"total_purchases": total_purchases,
"total_revenue": total_revenue,
"active_today": active_today,
"average_purchase_value": total_revenue / total_purchases if total_purchases > 0 else 0
}
def get_customer_list(self, segment: Optional[str] = None) -> List[Dict]:
"""Müşteri listesini getir"""
customers = []
for phone, customer in self.customers.items():
if segment and customer.get("segment") != segment:
continue
customers.append({
"phone": phone,
"name": customer.get("name", "Bilinmiyor"),
"segment": customer.get("segment", "Standart"),
"total_queries": customer.get("total_queries", 0),
"total_purchases": len(customer.get("purchases", [])),
"last_interaction": customer.get("last_interaction"),
"interests": customer.get("interests", [])
})
# Son etkileşime göre sırala
customers.sort(key=lambda x: x["last_interaction"], reverse=True)
return customers
def should_ask_for_name(self, phone: str, message: str) -> Tuple[bool, Optional[str]]:
"""İsim sorulmalı mı kontrol et"""
customer = self.customers.get(phone, {})
# Zaten isim varsa sorma
if customer.get("name"):
return False, None
queries = customer.get("total_queries", 0)
message_lower = message.lower()
# 1. Rezervasyon/satın alma anında (EN DOĞAL)
reservation_keywords = ["rezerv", "ayırt", "satın al", "alabilir miyim", "almak istiyorum",
"sipariş", "kargola", "gönder", "paket", "teslimat"]
if any(keyword in message_lower for keyword in reservation_keywords):
return True, "📝 Rezervasyon için adınızı alabilir miyim?"
# 2. Mağaza ziyareti planı
store_visit_keywords = ["mağazaya gel", "mağazaya uğra", "görmeye gel", "bakmaya gel",
"test sürüşü", "denemek istiyorum"]
if any(keyword in message_lower for keyword in store_visit_keywords):
return True, "🏪 Mağazada size daha iyi yardımcı olabilmemiz için adınızı öğrenebilir miyim?"
# 3. Telefon görüşmesi talebi
phone_keywords = ["ara", "telefon", "görüş", "konuş", "iletişim"]
if any(keyword in message_lower for keyword in phone_keywords):
return True, "📞 Sizi arayabilmemiz için adınızı paylaşır mısınız?"
# 4. Fiyat/ödeme görüşmesi (ciddi alıcı)
payment_keywords = ["taksit", "ödeme", "kredi kartı", "havale", "eft", "nakit", "peşin"]
if any(keyword in message_lower for keyword in payment_keywords):
return True, "💳 Size özel ödeme seçenekleri sunabilmem için adınızı öğrenebilir miyim?"
# 5. 3. mesajda nazikçe sor (sadece ürün sorguluyorsa)
if queries == 2: # 3. mesaj (0'dan başlıyor)
# Sadece basit selamlaşma değilse
greeting_only = ["merhaba", "selam", "günaydın", "iyi günler", "hey", "sa"]
if not any(message_lower.strip() == greeting for greeting in greeting_only):
return True, "Bu arada, size daha iyi hizmet verebilmem için adınızı öğrenebilir miyim? 😊"
# 6. 5+ sorgudan sonra hala isim yoksa (potansiyel müşteri)
if queries >= 5:
return True, "✨ Sizin için özel tekliflerimiz olabilir. Adınızı paylaşır mısınız?"
return False, None
def extract_name_from_response(self, message: str) -> Optional[str]:
"""İsim sorulduktan sonra gelen yanıttan isim çıkar"""
message_lower = message.lower().strip()
# Direkt isim yanıtları
if len(message_lower.split()) == 1:
# Yaygın olmayan tek kelime yanıtları filtrele
common_words = ["evet", "hayır", "tamam", "olur", "peki", "teşekkür", "teşekkürler",
"merhaba", "selam", "günaydın", "güle", "hoşçakal", "görüşürüz",
"sa", "as", "slm", "mrb", "ok", "okay", "yes", "no", "hi", "hello", "bye"]
if message_lower not in common_words:
# Tek kelime, muhtemelen isim
name = message.capitalize()
# Türkçe isim kontrolü (en az 2 karakter)
if len(name) >= 2 and name.isalpha():
return name
# "Benim adım X", "Ben X", "Adım X" kalıpları
patterns = [
r"(?:benim\s+)?ad[ıi]m?\s+(\w+)",
r"ben\s+(\w+)",
r"(\w+)\s+diye\s+hitap",
r"bana\s+(\w+)\s+diyebilirsiniz",
r"ismim\s+(\w+)",
r"(\w+),?\s*teşekkür", # "Ahmet, teşekkürler"
]
for pattern in patterns:
import re
match = re.search(pattern, message_lower)
if match:
name = match.group(1).capitalize()
if len(name) >= 2:
return name
# İsim + Soyisim durumu (sadece ismi al)
two_words = message.strip().split()
if len(two_words) == 2:
# Her ikisi de büyük harfle başlıyorsa
if two_words[0][0].isupper() and two_words[1][0].isupper():
return two_words[0]
return None