Restore backup and fix Turkish normalization
Browse files- Restored all Python files from backup (20250904)
- Fixed Turkish character normalization in smart_warehouse_with_price.py
- Removed CRM hardcoded keyword dependencies from app.py
- Improved product search with proper İ/I handling for Turkish XML data
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <[email protected]>
- __pycache__/prompts.cpython-312.pyc +0 -0
- __pycache__/smart_warehouse_with_price.cpython-312.pyc +0 -0
- app.py +39 -316
- smart_warehouse_with_price.py +13 -16
__pycache__/prompts.cpython-312.pyc
CHANGED
|
Binary files a/__pycache__/prompts.cpython-312.pyc and b/__pycache__/prompts.cpython-312.pyc differ
|
|
|
__pycache__/smart_warehouse_with_price.cpython-312.pyc
CHANGED
|
Binary files a/__pycache__/smart_warehouse_with_price.cpython-312.pyc and b/__pycache__/smart_warehouse_with_price.cpython-312.pyc differ
|
|
|
app.py
CHANGED
|
@@ -41,16 +41,6 @@ except ImportError:
|
|
| 41 |
USE_GPT5_SEARCH = False
|
| 42 |
logger.info("❌ GPT-5 search not available")
|
| 43 |
|
| 44 |
-
# Import CRM system
|
| 45 |
-
try:
|
| 46 |
-
from customer_manager import CustomerManager
|
| 47 |
-
customer_manager = CustomerManager()
|
| 48 |
-
USE_CRM = True
|
| 49 |
-
logger.info("✅ CRM System loaded - Customer tracking enabled")
|
| 50 |
-
except ImportError:
|
| 51 |
-
USE_CRM = False
|
| 52 |
-
logger.info("❌ CRM System not available")
|
| 53 |
-
|
| 54 |
warnings.simplefilter('ignore')
|
| 55 |
|
| 56 |
# Import Media Queue V2
|
|
@@ -758,10 +748,7 @@ def get_conversation_context(phone_number):
|
|
| 758 |
conversation_memory[phone_number] = {
|
| 759 |
"messages": [],
|
| 760 |
"current_category": None,
|
| 761 |
-
"last_activity": None
|
| 762 |
-
"last_product_info": None, # Son bulunan ürün bilgisi
|
| 763 |
-
"last_search_term": None, # Son arama terimi
|
| 764 |
-
"last_search_time": None # Son arama zamanı
|
| 765 |
}
|
| 766 |
return conversation_memory[phone_number]
|
| 767 |
|
|
@@ -784,81 +771,6 @@ def add_to_conversation(phone_number, user_message, ai_response):
|
|
| 784 |
|
| 785 |
detect_category(phone_number, user_message, ai_response)
|
| 786 |
|
| 787 |
-
def cache_product_info(phone_number, search_term, product_info):
|
| 788 |
-
"""Ürün bilgisini conversation memory'e cache'le"""
|
| 789 |
-
import datetime
|
| 790 |
-
|
| 791 |
-
context = get_conversation_context(phone_number)
|
| 792 |
-
context["last_product_info"] = product_info
|
| 793 |
-
context["last_search_term"] = search_term.lower().strip()
|
| 794 |
-
context["last_search_time"] = datetime.datetime.now()
|
| 795 |
-
|
| 796 |
-
logger.info(f"📦 Cached product info for {phone_number}: {search_term}")
|
| 797 |
-
|
| 798 |
-
def get_cached_product_info(phone_number, search_term):
|
| 799 |
-
"""Cache'den ürün bilgisini getir"""
|
| 800 |
-
import datetime
|
| 801 |
-
|
| 802 |
-
context = get_conversation_context(phone_number)
|
| 803 |
-
|
| 804 |
-
if not context.get("last_product_info") or not context.get("last_search_term"):
|
| 805 |
-
return None
|
| 806 |
-
|
| 807 |
-
# Son aramadan 30 dakika geçmişse cache'i temizle
|
| 808 |
-
if context.get("last_search_time"):
|
| 809 |
-
time_diff = datetime.datetime.now() - context["last_search_time"]
|
| 810 |
-
if time_diff.total_seconds() > 1800: # 30 dakika
|
| 811 |
-
context["last_product_info"] = None
|
| 812 |
-
return None
|
| 813 |
-
|
| 814 |
-
# Arama terimi benzer mi kontrol et
|
| 815 |
-
cached_term = context["last_search_term"]
|
| 816 |
-
current_term = search_term.lower().strip()
|
| 817 |
-
|
| 818 |
-
# Daha esnek ürün eşleştirmesi
|
| 819 |
-
|
| 820 |
-
# 1. Aynı ürün ailesi tespit edici kelimeler
|
| 821 |
-
product_families = ["fx", "marlin", "domane", "madone", "emonda", "fuel", "supercaliber", "procaliber"]
|
| 822 |
-
|
| 823 |
-
# Cache'deki ürün ailesini bul
|
| 824 |
-
cached_family = None
|
| 825 |
-
for family in product_families:
|
| 826 |
-
if family in cached_term:
|
| 827 |
-
cached_family = family
|
| 828 |
-
break
|
| 829 |
-
|
| 830 |
-
# Current term'deki ürün ailesini bul
|
| 831 |
-
current_family = None
|
| 832 |
-
for family in product_families:
|
| 833 |
-
if family in current_term:
|
| 834 |
-
current_family = family
|
| 835 |
-
break
|
| 836 |
-
|
| 837 |
-
# Aynı ürün ailesi varsa cache kullan
|
| 838 |
-
if cached_family and current_family and cached_family == current_family:
|
| 839 |
-
logger.info(f"📦 Using cached product info - Same family: {cached_family}")
|
| 840 |
-
return context["last_product_info"]
|
| 841 |
-
|
| 842 |
-
# 2. Context-aware keywords - sadece farklı ürün ismi içermiyorsa
|
| 843 |
-
contextual_keywords = ["boyum", "boy", "beden", "size", "l ", "xl", "cm", "ayırt", "rezerv",
|
| 844 |
-
"fiyat", "price", "stok", "nasıl", "nerede", "hangi mağaza",
|
| 845 |
-
"teslimat", "ödeme", "taksit", "kaç", "almak", "satın"]
|
| 846 |
-
|
| 847 |
-
# Farklı bir ürün ailesi ismi var mı kontrol et
|
| 848 |
-
has_different_product = current_family and current_family != cached_family
|
| 849 |
-
|
| 850 |
-
# Cache'de ürün var, farklı ürün yok ve contextual ise
|
| 851 |
-
if cached_family and not has_different_product and any(keyword in current_term for keyword in contextual_keywords):
|
| 852 |
-
logger.info(f"📦 Using cached product info - Contextual query about {cached_family}")
|
| 853 |
-
return context["last_product_info"]
|
| 854 |
-
|
| 855 |
-
# 3. Tam metin benzerliği (eski mantık)
|
| 856 |
-
if cached_term in current_term or current_term in cached_term:
|
| 857 |
-
logger.info(f"📦 Using cached product info - Text similarity: {search_term}")
|
| 858 |
-
return context["last_product_info"]
|
| 859 |
-
|
| 860 |
-
return None
|
| 861 |
-
|
| 862 |
def detect_category(phone_number, user_message, ai_response):
|
| 863 |
"""Konuşulan kategoriyi tespit et"""
|
| 864 |
context = get_conversation_context(phone_number)
|
|
@@ -1033,18 +945,6 @@ def process_whatsapp_message_with_media(user_message, phone_number, media_urls,
|
|
| 1033 |
def process_whatsapp_message_with_memory(user_message, phone_number):
|
| 1034 |
"""Hafızalı WhatsApp mesaj işleme"""
|
| 1035 |
try:
|
| 1036 |
-
# CRM - Kişiselleştirilmiş yanıt hazırlığı
|
| 1037 |
-
personalized_greeting = ""
|
| 1038 |
-
product_searched = None
|
| 1039 |
-
|
| 1040 |
-
if USE_CRM:
|
| 1041 |
-
clean_phone = phone_number.replace('whatsapp:', '')
|
| 1042 |
-
customer_context = customer_manager.get_customer_context(clean_phone)
|
| 1043 |
-
|
| 1044 |
-
# Eğer ilk mesaj ise veya selamlama içeriyorsa
|
| 1045 |
-
user_msg_lower = user_message.lower()
|
| 1046 |
-
if any(word in user_msg_lower for word in ["merhaba", "selam", "günaydın", "iyi günler", "iyi akşamlar", "hey", "hi", "hello"]):
|
| 1047 |
-
personalized_greeting = customer_context.get("greeting", "") + "\n\n"
|
| 1048 |
# 🔔 Yeni Mağaza Bildirim Sistemi - Mehmet Bey'e otomatik bildirim
|
| 1049 |
if USE_STORE_NOTIFICATION:
|
| 1050 |
# Önce basit keyword kontrolü yap
|
|
@@ -1195,56 +1095,61 @@ def process_whatsapp_message_with_memory(user_message, phone_number):
|
|
| 1195 |
|
| 1196 |
if warehouse_info_parts:
|
| 1197 |
warehouse_response = "\n".join(warehouse_info_parts)
|
| 1198 |
-
|
| 1199 |
-
# Cache'e kaydet - tutarlılık için
|
| 1200 |
-
cache_product_info(phone_number, user_message, warehouse_response)
|
| 1201 |
-
|
| 1202 |
messages.append({
|
| 1203 |
"role": "system",
|
| 1204 |
"content": f"MAĞAZA STOK BİLGİSİ (BF Space):\n{warehouse_response}\n\nSADECE bu bilgileri kullanarak kullanıcıya yardımcı ol."
|
| 1205 |
})
|
| 1206 |
product_found_improved = True
|
| 1207 |
-
logger.info("✅ BF Space: Warehouse stock info used
|
| 1208 |
|
| 1209 |
if not product_found_improved:
|
| 1210 |
-
# Use improved search response directly
|
| 1211 |
-
response_content = product_result['response']
|
| 1212 |
-
|
| 1213 |
-
# Cache'e kaydet - tutarlılık için
|
| 1214 |
-
cache_product_info(phone_number, user_message, response_content)
|
| 1215 |
-
|
| 1216 |
messages.append({
|
| 1217 |
"role": "system",
|
| 1218 |
-
"content": f"ÜRÜN BİLGİSİ (BF Space):\n{
|
| 1219 |
})
|
| 1220 |
product_found_improved = True
|
| 1221 |
-
logger.info("✅ BF Space: Improved product search used
|
| 1222 |
except Exception as e:
|
| 1223 |
logger.error(f"❌ BF Space: Improved search error: {e}")
|
| 1224 |
|
| 1225 |
-
#
|
| 1226 |
if not product_found_improved:
|
| 1227 |
-
#
|
| 1228 |
-
|
|
|
|
| 1229 |
|
| 1230 |
-
|
| 1231 |
-
|
| 1232 |
-
|
| 1233 |
-
|
| 1234 |
-
|
| 1235 |
-
|
| 1236 |
-
|
| 1237 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1238 |
else:
|
| 1239 |
-
|
| 1240 |
-
# System prompt'taki "MESAJ TİPİ TANIMA REHBERİ"ni kullansın
|
| 1241 |
-
|
| 1242 |
-
# GPT-5'e özel talimat: Eğer ürün bilgisi gerekiyorsa belirtsin
|
| 1243 |
-
messages.append({
|
| 1244 |
-
"role": "system",
|
| 1245 |
-
"content": "NOT: Eğer bu mesaj bir ürün/stok/fiyat sorgusu ise ve elimde o bilgi yoksa, kullanıcıya 'Bu ürün için güncel bilgi kontrol edebilirim, ister misiniz?' diye sor. Basit selamlamalarda stok araması gereksiz."
|
| 1246 |
-
})
|
| 1247 |
-
logger.info("🧠 GPT-5'e mesaj klasifikasyonu bırakıldı - hardcoded logic yok")
|
| 1248 |
|
| 1249 |
if not OPENAI_API_KEY:
|
| 1250 |
return "OpenAI API anahtarı eksik. Lütfen environment variables'ları kontrol edin."
|
|
@@ -1281,54 +1186,6 @@ def process_whatsapp_message_with_memory(user_message, phone_number):
|
|
| 1281 |
# WhatsApp için resim URL'lerini formatla
|
| 1282 |
formatted_response = extract_product_info_whatsapp(ai_response)
|
| 1283 |
|
| 1284 |
-
# CRM - Kişiselleştirilmiş selamlama ekle (sadece GPT-5 selamlamadıysa)
|
| 1285 |
-
if personalized_greeting:
|
| 1286 |
-
# GPT-5 zaten selamladıysa ekleme
|
| 1287 |
-
ai_lower = ai_response.lower()
|
| 1288 |
-
if not any(word in ai_lower for word in ["hoş geldin", "selam", "merhaba", "iyi akşam", "günaydın", "iyi günler"]):
|
| 1289 |
-
formatted_response = personalized_greeting + formatted_response
|
| 1290 |
-
|
| 1291 |
-
# CRM - İsim sorma kontrolü
|
| 1292 |
-
name_request = ""
|
| 1293 |
-
if USE_CRM:
|
| 1294 |
-
clean_phone = phone_number.replace('whatsapp:', '')
|
| 1295 |
-
|
| 1296 |
-
# Önce mevcut müşteriyi kontrol et (son mesajı isim yanıtı olabilir)
|
| 1297 |
-
customer = customer_manager.get_or_create_customer(clean_phone)
|
| 1298 |
-
|
| 1299 |
-
# Eğer önceki mesajda isim sorulmuşsa ve şimdi yanıt geliyorsa
|
| 1300 |
-
if customer.get("awaiting_name_response", False):
|
| 1301 |
-
# İsim çıkarmayı dene
|
| 1302 |
-
extracted_name = customer_manager.extract_name_from_response(user_message)
|
| 1303 |
-
if extracted_name:
|
| 1304 |
-
customer["name"] = extracted_name
|
| 1305 |
-
customer["awaiting_name_response"] = False
|
| 1306 |
-
customer_manager._save_database()
|
| 1307 |
-
formatted_response = f"Memnun oldum {extracted_name}!\n\n" + formatted_response
|
| 1308 |
-
logger.info(f"✅ İsim kaydedildi: {extracted_name}")
|
| 1309 |
-
|
| 1310 |
-
# İsim sorulmalı mı kontrol et
|
| 1311 |
-
should_ask, name_question = customer_manager.should_ask_for_name(clean_phone, user_message)
|
| 1312 |
-
if should_ask:
|
| 1313 |
-
name_request = f"\n\n{name_question}"
|
| 1314 |
-
# İsim yanıtı bekleniyor işaretle
|
| 1315 |
-
customer["awaiting_name_response"] = True
|
| 1316 |
-
customer_manager._save_database()
|
| 1317 |
-
|
| 1318 |
-
# Ürün araması yapılmışsa, ürün adını çıkar
|
| 1319 |
-
if "📦" in formatted_response or "stok" in formatted_response.lower():
|
| 1320 |
-
# İlk satırdan ürün adını çıkarmaya çalış
|
| 1321 |
-
lines = formatted_response.split('\n')
|
| 1322 |
-
for line in lines:
|
| 1323 |
-
if '📦' in line or 'Trek' in line or 'Marlin' in line or 'FX' in line:
|
| 1324 |
-
product_searched = line.replace('📦', '').strip()
|
| 1325 |
-
break
|
| 1326 |
-
customer_manager.update_interaction(clean_phone, user_message, product_searched)
|
| 1327 |
-
|
| 1328 |
-
# İsim sorusunu yanıta ekle
|
| 1329 |
-
if name_request:
|
| 1330 |
-
formatted_response = formatted_response + name_request
|
| 1331 |
-
|
| 1332 |
# Sohbet geçmişine ekle
|
| 1333 |
add_to_conversation(phone_number, user_message, formatted_response)
|
| 1334 |
|
|
@@ -1645,21 +1502,6 @@ async def whatsapp_webhook(request: Request):
|
|
| 1645 |
|
| 1646 |
print(f"✅ MESAJ ALINDI: {from_number} -> {message_body}")
|
| 1647 |
|
| 1648 |
-
# CRM - Müşteri tanıma ve güncelleme
|
| 1649 |
-
if USE_CRM:
|
| 1650 |
-
# Telefon numarasından whatsapp: prefix'ini kaldır
|
| 1651 |
-
clean_phone = from_number.replace('whatsapp:', '')
|
| 1652 |
-
|
| 1653 |
-
# Müşteri bilgilerini al veya oluştur
|
| 1654 |
-
customer = customer_manager.get_or_create_customer(clean_phone)
|
| 1655 |
-
customer_context = customer_manager.get_customer_context(clean_phone)
|
| 1656 |
-
|
| 1657 |
-
# Müşteri segmenti ve kişisel bilgi log
|
| 1658 |
-
if customer_context.get("name"):
|
| 1659 |
-
logger.info(f"👤 Müşteri: {customer_context['name']} ({customer_context['segment']} segment)")
|
| 1660 |
-
else:
|
| 1661 |
-
logger.info(f"👤 Müşteri: {clean_phone} ({customer_context['segment']} segment)")
|
| 1662 |
-
|
| 1663 |
if not twilio_client:
|
| 1664 |
return {"status": "error", "message": "Twilio yapılandırması eksik"}
|
| 1665 |
|
|
@@ -1731,125 +1573,6 @@ async def clear_memory(phone_number: str):
|
|
| 1731 |
return {"status": "info", "message": "Hafıza bulunamadı"}
|
| 1732 |
|
| 1733 |
# Mehmet Bey bildirimlerini görüntüleme endpoint'i
|
| 1734 |
-
@app.get("/crm-analytics")
|
| 1735 |
-
async def view_crm_analytics():
|
| 1736 |
-
"""CRM analytics ve müşteri listesi görüntüle"""
|
| 1737 |
-
if not USE_CRM:
|
| 1738 |
-
return {"error": "CRM system not available"}
|
| 1739 |
-
|
| 1740 |
-
analytics = customer_manager.get_analytics()
|
| 1741 |
-
customers = customer_manager.get_customer_list()
|
| 1742 |
-
|
| 1743 |
-
# HTML formatında döndür
|
| 1744 |
-
html_content = f"""
|
| 1745 |
-
<!DOCTYPE html>
|
| 1746 |
-
<html>
|
| 1747 |
-
<head>
|
| 1748 |
-
<title>CRM Analytics - Trek Bisiklet</title>
|
| 1749 |
-
<meta charset="utf-8">
|
| 1750 |
-
<style>
|
| 1751 |
-
body {{ font-family: Arial, sans-serif; padding: 20px; background: #f5f5f5; }}
|
| 1752 |
-
h1 {{ color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; }}
|
| 1753 |
-
.stats-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; }}
|
| 1754 |
-
.stat-card {{ background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }}
|
| 1755 |
-
.stat-number {{ font-size: 2em; font-weight: bold; color: #007bff; }}
|
| 1756 |
-
.stat-label {{ color: #666; margin-top: 5px; }}
|
| 1757 |
-
.segment-chart {{ background: white; padding: 20px; border-radius: 8px; margin: 20px 0; }}
|
| 1758 |
-
table {{ width: 100%; background: white; border-radius: 8px; overflow: hidden; margin: 20px 0; }}
|
| 1759 |
-
th {{ background: #007bff; color: white; padding: 12px; text-align: left; }}
|
| 1760 |
-
td {{ padding: 12px; border-bottom: 1px solid #eee; }}
|
| 1761 |
-
tr:hover {{ background: #f9f9f9; }}
|
| 1762 |
-
.segment-badge {{ padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: bold; }}
|
| 1763 |
-
.segment-vip {{ background: #ffd700; color: #333; }}
|
| 1764 |
-
.segment-potansiyel {{ background: #17a2b8; color: white; }}
|
| 1765 |
-
.segment-yeni {{ background: #28a745; color: white; }}
|
| 1766 |
-
.segment-kayip {{ background: #dc3545; color: white; }}
|
| 1767 |
-
.segment-aktif {{ background: #007bff; color: white; }}
|
| 1768 |
-
.segment-standart {{ background: #6c757d; color: white; }}
|
| 1769 |
-
</style>
|
| 1770 |
-
</head>
|
| 1771 |
-
<body>
|
| 1772 |
-
<h1>🎯 CRM Analytics Dashboard</h1>
|
| 1773 |
-
|
| 1774 |
-
<div class="stats-grid">
|
| 1775 |
-
<div class="stat-card">
|
| 1776 |
-
<div class="stat-number">{analytics['total_customers']}</div>
|
| 1777 |
-
<div class="stat-label">Toplam Müşteri</div>
|
| 1778 |
-
</div>
|
| 1779 |
-
<div class="stat-card">
|
| 1780 |
-
<div class="stat-number">{analytics['active_today']}</div>
|
| 1781 |
-
<div class="stat-label">Bugün Aktif</div>
|
| 1782 |
-
</div>
|
| 1783 |
-
<div class="stat-card">
|
| 1784 |
-
<div class="stat-number">{analytics['total_purchases']}</div>
|
| 1785 |
-
<div class="stat-label">Toplam Satış</div>
|
| 1786 |
-
</div>
|
| 1787 |
-
<div class="stat-card">
|
| 1788 |
-
<div class="stat-number">₺{analytics['total_revenue']:,.0f}</div>
|
| 1789 |
-
<div class="stat-label">Toplam Gelir</div>
|
| 1790 |
-
</div>
|
| 1791 |
-
</div>
|
| 1792 |
-
|
| 1793 |
-
<div class="segment-chart">
|
| 1794 |
-
<h2>📊 Müşteri Segmentleri</h2>
|
| 1795 |
-
<div class="stats-grid">
|
| 1796 |
-
"""
|
| 1797 |
-
|
| 1798 |
-
for segment, count in analytics['segments'].items():
|
| 1799 |
-
html_content += f"""
|
| 1800 |
-
<div class="stat-card">
|
| 1801 |
-
<div class="stat-number">{count}</div>
|
| 1802 |
-
<div class="stat-label segment-badge segment-{segment.lower().replace(' ', '-')}">{segment}</div>
|
| 1803 |
-
</div>
|
| 1804 |
-
"""
|
| 1805 |
-
|
| 1806 |
-
html_content += """
|
| 1807 |
-
</div>
|
| 1808 |
-
</div>
|
| 1809 |
-
|
| 1810 |
-
<h2>👥 Müşteri Listesi</h2>
|
| 1811 |
-
<table>
|
| 1812 |
-
<thead>
|
| 1813 |
-
<tr>
|
| 1814 |
-
<th>Telefon</th>
|
| 1815 |
-
<th>İsim</th>
|
| 1816 |
-
<th>Segment</th>
|
| 1817 |
-
<th>Sorgular</th>
|
| 1818 |
-
<th>Satınalmalar</th>
|
| 1819 |
-
<th>Son Etkileşim</th>
|
| 1820 |
-
<th>İlgi Alanları</th>
|
| 1821 |
-
</tr>
|
| 1822 |
-
</thead>
|
| 1823 |
-
<tbody>
|
| 1824 |
-
"""
|
| 1825 |
-
|
| 1826 |
-
for customer in customers[:50]: # İlk 50 müşteri
|
| 1827 |
-
segment_class = f"segment-{customer['segment'].lower().replace(' ', '-')}"
|
| 1828 |
-
interests = ', '.join(customer['interests'][:3]) if customer['interests'] else '-'
|
| 1829 |
-
last_interaction = customer['last_interaction'].split('T')[0] if customer['last_interaction'] else '-'
|
| 1830 |
-
|
| 1831 |
-
html_content += f"""
|
| 1832 |
-
<tr>
|
| 1833 |
-
<td>{customer['phone']}</td>
|
| 1834 |
-
<td>{customer['name']}</td>
|
| 1835 |
-
<td><span class="segment-badge {segment_class}">{customer['segment']}</span></td>
|
| 1836 |
-
<td>{customer['total_queries']}</td>
|
| 1837 |
-
<td>{customer['total_purchases']}</td>
|
| 1838 |
-
<td>{last_interaction}</td>
|
| 1839 |
-
<td>{interests}</td>
|
| 1840 |
-
</tr>
|
| 1841 |
-
"""
|
| 1842 |
-
|
| 1843 |
-
html_content += """
|
| 1844 |
-
</tbody>
|
| 1845 |
-
</table>
|
| 1846 |
-
</body>
|
| 1847 |
-
</html>
|
| 1848 |
-
"""
|
| 1849 |
-
|
| 1850 |
-
from fastapi.responses import HTMLResponse
|
| 1851 |
-
return HTMLResponse(content=html_content)
|
| 1852 |
-
|
| 1853 |
@app.get("/mehmet-bey-notifications")
|
| 1854 |
async def view_mehmet_bey_notifications():
|
| 1855 |
"""Mehmet Bey için kaydedilen bildirimleri görüntüle"""
|
|
|
|
| 41 |
USE_GPT5_SEARCH = False
|
| 42 |
logger.info("❌ GPT-5 search not available")
|
| 43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
warnings.simplefilter('ignore')
|
| 45 |
|
| 46 |
# Import Media Queue V2
|
|
|
|
| 748 |
conversation_memory[phone_number] = {
|
| 749 |
"messages": [],
|
| 750 |
"current_category": None,
|
| 751 |
+
"last_activity": None
|
|
|
|
|
|
|
|
|
|
| 752 |
}
|
| 753 |
return conversation_memory[phone_number]
|
| 754 |
|
|
|
|
| 771 |
|
| 772 |
detect_category(phone_number, user_message, ai_response)
|
| 773 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 774 |
def detect_category(phone_number, user_message, ai_response):
|
| 775 |
"""Konuşulan kategoriyi tespit et"""
|
| 776 |
context = get_conversation_context(phone_number)
|
|
|
|
| 945 |
def process_whatsapp_message_with_memory(user_message, phone_number):
|
| 946 |
"""Hafızalı WhatsApp mesaj işleme"""
|
| 947 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 948 |
# 🔔 Yeni Mağaza Bildirim Sistemi - Mehmet Bey'e otomatik bildirim
|
| 949 |
if USE_STORE_NOTIFICATION:
|
| 950 |
# Önce basit keyword kontrolü yap
|
|
|
|
| 1095 |
|
| 1096 |
if warehouse_info_parts:
|
| 1097 |
warehouse_response = "\n".join(warehouse_info_parts)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1098 |
messages.append({
|
| 1099 |
"role": "system",
|
| 1100 |
"content": f"MAĞAZA STOK BİLGİSİ (BF Space):\n{warehouse_response}\n\nSADECE bu bilgileri kullanarak kullanıcıya yardımcı ol."
|
| 1101 |
})
|
| 1102 |
product_found_improved = True
|
| 1103 |
+
logger.info("✅ BF Space: Warehouse stock info used")
|
| 1104 |
|
| 1105 |
if not product_found_improved:
|
| 1106 |
+
# Use improved search response directly
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1107 |
messages.append({
|
| 1108 |
"role": "system",
|
| 1109 |
+
"content": f"ÜRÜN BİLGİSİ (BF Space):\n{product_result['response']}\n\nSADECE bu bilgileri kullanarak kullanıcıya yardımcı ol. Bu bilgiler dışında ek bilgi ekleme."
|
| 1110 |
})
|
| 1111 |
product_found_improved = True
|
| 1112 |
+
logger.info("✅ BF Space: Improved product search used")
|
| 1113 |
except Exception as e:
|
| 1114 |
logger.error(f"❌ BF Space: Improved search error: {e}")
|
| 1115 |
|
| 1116 |
+
# Fallback to warehouse search if improved search didn't work
|
| 1117 |
if not product_found_improved:
|
| 1118 |
+
# Check if message seems to be asking about products
|
| 1119 |
+
product_keywords = ['fiyat', 'kaç', 'stok', 'var mı', 'mevcut', 'bisiklet', 'bike',
|
| 1120 |
+
'trek', 'model', 'beden', 'renk', 'mağaza', 'nerede', 'hangi']
|
| 1121 |
|
| 1122 |
+
# Common non-product responses
|
| 1123 |
+
non_product_responses = ['süper', 'harika', 'güzel', 'teşekkür', 'tamam', 'olur',
|
| 1124 |
+
'evet', 'hayır', 'peki', 'anladım', 'tamamdır']
|
| 1125 |
+
|
| 1126 |
+
is_product_query = False
|
| 1127 |
+
lower_message = user_message.lower()
|
| 1128 |
+
|
| 1129 |
+
# Check if it's likely a product query
|
| 1130 |
+
if any(keyword in lower_message for keyword in product_keywords):
|
| 1131 |
+
is_product_query = True
|
| 1132 |
+
# Check if it's NOT a simple response
|
| 1133 |
+
elif lower_message not in non_product_responses and len(lower_message.split()) > 1:
|
| 1134 |
+
# Multi-word queries might be product searches
|
| 1135 |
+
is_product_query = True
|
| 1136 |
+
# Single short words are usually not products
|
| 1137 |
+
elif len(lower_message.split()) == 1 and len(lower_message) < 6:
|
| 1138 |
+
is_product_query = False
|
| 1139 |
+
|
| 1140 |
+
if is_product_query:
|
| 1141 |
+
# Use GPT-5 warehouse search for product queries
|
| 1142 |
+
logger.info("📦 Using GPT-5 warehouse search")
|
| 1143 |
+
warehouse_result = get_warehouse_stock(user_message)
|
| 1144 |
+
if warehouse_result and warehouse_result != ['Ürün bulunamadı']:
|
| 1145 |
+
warehouse_response = "\n".join(warehouse_result)
|
| 1146 |
+
messages.append({
|
| 1147 |
+
"role": "system",
|
| 1148 |
+
"content": f"MAĞAZA STOK BİLGİSİ:\n{warehouse_response}\n\nBu bilgileri kullanarak kullanıcıya yardımcı ol. Fiyat ve stok bilgilerini AYNEN kullan."
|
| 1149 |
+
})
|
| 1150 |
+
logger.info(f"✅ Warehouse stock info added: {warehouse_response[:200]}...")
|
| 1151 |
else:
|
| 1152 |
+
logger.info(f"🚫 Skipping product search for: '{user_message}'")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1153 |
|
| 1154 |
if not OPENAI_API_KEY:
|
| 1155 |
return "OpenAI API anahtarı eksik. Lütfen environment variables'ları kontrol edin."
|
|
|
|
| 1186 |
# WhatsApp için resim URL'lerini formatla
|
| 1187 |
formatted_response = extract_product_info_whatsapp(ai_response)
|
| 1188 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1189 |
# Sohbet geçmişine ekle
|
| 1190 |
add_to_conversation(phone_number, user_message, formatted_response)
|
| 1191 |
|
|
|
|
| 1502 |
|
| 1503 |
print(f"✅ MESAJ ALINDI: {from_number} -> {message_body}")
|
| 1504 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1505 |
if not twilio_client:
|
| 1506 |
return {"status": "error", "message": "Twilio yapılandırması eksik"}
|
| 1507 |
|
|
|
|
| 1573 |
return {"status": "info", "message": "Hafıza bulunamadı"}
|
| 1574 |
|
| 1575 |
# Mehmet Bey bildirimlerini görüntüleme endpoint'i
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1576 |
@app.get("/mehmet-bey-notifications")
|
| 1577 |
async def view_mehmet_bey_notifications():
|
| 1578 |
"""Mehmet Bey için kaydedilen bildirimleri görüntüle"""
|
smart_warehouse_with_price.py
CHANGED
|
@@ -293,26 +293,23 @@ def get_warehouse_stock_smart_with_price(user_message, previous_result=None):
|
|
| 293 |
# Check if the target product exists
|
| 294 |
# Normalize Turkish characters for comparison
|
| 295 |
def normalize_turkish(text):
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
#
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
'Ğ': 'G', 'ğ': 'g', 'Ü': 'U', 'ü': 'u',
|
| 302 |
-
'Ö': 'O', 'ö': 'o', 'Ç': 'C', 'ç': 'c'
|
| 303 |
-
}
|
| 304 |
-
normalized = text
|
| 305 |
-
for tr_char, en_char in replacements.items():
|
| 306 |
-
normalized = normalized.replace(tr_char, en_char)
|
| 307 |
-
return normalized.lower()
|
| 308 |
|
| 309 |
-
search_term =
|
|
|
|
| 310 |
|
| 311 |
matching_products = []
|
| 312 |
for p in products_summary:
|
| 313 |
-
p_name =
|
| 314 |
-
# Check
|
| 315 |
-
if search_term in p_name
|
|
|
|
|
|
|
|
|
|
| 316 |
matching_products.append(p)
|
| 317 |
|
| 318 |
if matching_products:
|
|
|
|
| 293 |
# Check if the target product exists
|
| 294 |
# Normalize Turkish characters for comparison
|
| 295 |
def normalize_turkish(text):
|
| 296 |
+
text = text.upper()
|
| 297 |
+
replacements = {'I': 'İ', 'Ç': 'C', 'Ş': 'S', 'Ğ': 'G', 'Ü': 'U', 'Ö': 'O'}
|
| 298 |
+
# Also try with İ -> I conversion
|
| 299 |
+
text2 = text.replace('İ', 'I')
|
| 300 |
+
return text, text2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
|
| 302 |
+
search_term = user_message.upper()
|
| 303 |
+
search_norm1, search_norm2 = normalize_turkish(search_term)
|
| 304 |
|
| 305 |
matching_products = []
|
| 306 |
for p in products_summary:
|
| 307 |
+
p_name = p['name'].upper()
|
| 308 |
+
# Check both original and normalized versions
|
| 309 |
+
if (search_term in p_name or
|
| 310 |
+
search_norm1 in p_name or
|
| 311 |
+
search_norm2 in p_name or
|
| 312 |
+
search_term.replace('I', 'İ') in p_name):
|
| 313 |
matching_products.append(p)
|
| 314 |
|
| 315 |
if matching_products:
|