|
|
import os |
|
|
import json |
|
|
import requests |
|
|
import xml.etree.ElementTree as ET |
|
|
import warnings |
|
|
from fastapi import FastAPI, Request |
|
|
from twilio.rest import Client |
|
|
from twilio.twiml.messaging_response import MessagingResponse |
|
|
|
|
|
|
|
|
from prompts import get_prompt_content_only |
|
|
from whatsapp_features import ( |
|
|
initialize_whatsapp_features, handle_whatsapp_comparison_request, |
|
|
handle_whatsapp_budget_request, handle_whatsapp_category_request |
|
|
) |
|
|
from whatsapp_renderer import extract_product_info_whatsapp |
|
|
from whatsapp_passive_profiler import ( |
|
|
analyze_user_message, get_user_profile_summary, get_personalized_recommendations |
|
|
) |
|
|
|
|
|
|
|
|
import logging |
|
|
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
warnings.simplefilter('ignore') |
|
|
|
|
|
|
|
|
API_URL = "https://api.openai.com/v1/chat/completions" |
|
|
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") |
|
|
logger.info(f"OpenAI API Key var mı: {'Evet' if OPENAI_API_KEY else 'Hayır'}") |
|
|
|
|
|
|
|
|
TWILIO_ACCOUNT_SID = os.getenv("TWILIO_ACCOUNT_SID") |
|
|
TWILIO_AUTH_TOKEN = os.getenv("TWILIO_AUTH_TOKEN") |
|
|
TWILIO_MESSAGING_SERVICE_SID = os.getenv("TWILIO_MESSAGING_SERVICE_SID") |
|
|
TWILIO_WHATSAPP_NUMBER = "whatsapp:+905332047254" |
|
|
|
|
|
logger.info(f"Twilio SID var mı: {'Evet' if TWILIO_ACCOUNT_SID else 'Hayır'}") |
|
|
logger.info(f"Twilio Auth Token var mı: {'Evet' if TWILIO_AUTH_TOKEN else 'Hayır'}") |
|
|
logger.info(f"Messaging Service SID var mı: {'Evet' if TWILIO_MESSAGING_SERVICE_SID else 'Hayır'}") |
|
|
|
|
|
if not TWILIO_ACCOUNT_SID or not TWILIO_AUTH_TOKEN: |
|
|
logger.error("❌ Twilio bilgileri eksik!") |
|
|
twilio_client = None |
|
|
else: |
|
|
try: |
|
|
twilio_client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN) |
|
|
logger.info("✅ Twilio client başarıyla oluşturuldu!") |
|
|
except Exception as e: |
|
|
logger.error(f"❌ Twilio client hatası: {e}") |
|
|
twilio_client = None |
|
|
|
|
|
|
|
|
try: |
|
|
print("🔍 XML DEBUG TEST BAŞLADI") |
|
|
print("========================") |
|
|
|
|
|
url = 'https://www.trekbisiklet.com.tr/output/8582384479' |
|
|
print(f"📡 URL: {url}") |
|
|
|
|
|
print("📥 HTTP isteği gönderiliyor...") |
|
|
response = requests.get(url, verify=False, timeout=10) |
|
|
print(f"✅ HTTP Status: {response.status_code}") |
|
|
print(f"📏 Content Length: {len(response.content)} bytes") |
|
|
|
|
|
|
|
|
content_preview = response.content[:500].decode('utf-8', errors='ignore') |
|
|
print(f"📄 İlk 500 karakter:") |
|
|
print(content_preview) |
|
|
print("...") |
|
|
|
|
|
print("\n🔄 XML parsing...") |
|
|
root = ET.fromstring(response.content) |
|
|
print(f"✅ XML root tag: {root.tag}") |
|
|
|
|
|
all_items = root.findall('item') |
|
|
print(f"📦 Toplam item sayısı: {len(all_items)}") |
|
|
|
|
|
|
|
|
print(f"\n🔍 İlk 3 item analizi:") |
|
|
for i, item in enumerate(all_items[:3]): |
|
|
print(f"\n--- ITEM {i+1} ---") |
|
|
|
|
|
rootlabel = item.find('rootlabel') |
|
|
if rootlabel is not None and rootlabel.text: |
|
|
print(f"📛 Ürün: {rootlabel.text}") |
|
|
else: |
|
|
print("❌ rootlabel bulunamadı") |
|
|
|
|
|
stock = item.find('stockAmount') |
|
|
if stock is not None and stock.text: |
|
|
print(f"📦 Stok: {stock.text}") |
|
|
else: |
|
|
print("❌ stockAmount bulunamadı") |
|
|
|
|
|
price = item.find('priceTaxWithCur') |
|
|
if price is not None and price.text: |
|
|
print(f"💰 Fiyat: {price.text}") |
|
|
else: |
|
|
print("❌ priceTaxWithCur bulunamadı") |
|
|
|
|
|
|
|
|
print(f"\n🎯 MARLIN ARAMA TESİ:") |
|
|
marlin_count = 0 |
|
|
products = [] |
|
|
|
|
|
for item in all_items: |
|
|
|
|
|
stock_number = 0 |
|
|
stock_amount = "stokta değil" |
|
|
price = "" |
|
|
price_eft = "" |
|
|
product_link = "" |
|
|
|
|
|
rootlabel = item.find('rootlabel') |
|
|
if rootlabel is None or not rootlabel.text: |
|
|
continue |
|
|
|
|
|
full_name = rootlabel.text.strip() |
|
|
name_words = full_name.lower().split() |
|
|
name = name_words[0] if name_words else "unknown" |
|
|
|
|
|
|
|
|
stock_element = item.find('stockAmount') |
|
|
if stock_element is not None and stock_element.text: |
|
|
try: |
|
|
stock_number = int(stock_element.text.strip()) |
|
|
stock_amount = "stokta" if stock_number > 0 else "stokta değil" |
|
|
except (ValueError, TypeError): |
|
|
stock_number = 0 |
|
|
stock_amount = "stokta değil" |
|
|
|
|
|
|
|
|
if 'marlin' in full_name.lower(): |
|
|
marlin_count += 1 |
|
|
print(f"🎯 MARLIN BULUNDU #{marlin_count}: {full_name}") |
|
|
print(f" 📦 Stok: {stock_number} adet") |
|
|
print(f" 📊 Durum: {stock_amount}") |
|
|
|
|
|
|
|
|
if stock_amount == "stokta": |
|
|
|
|
|
price_element = item.find('priceTaxWithCur') |
|
|
price_str = price_element.text if price_element is not None and price_element.text else "0" |
|
|
|
|
|
|
|
|
price_eft_element = item.find('priceEft') |
|
|
price_eft_str = price_eft_element.text if price_eft_element is not None and price_eft_element.text else "" |
|
|
|
|
|
|
|
|
link_element = item.find('productLink') |
|
|
product_link = link_element.text if link_element is not None and link_element.text else "" |
|
|
|
|
|
|
|
|
try: |
|
|
price_float = float(price_str) |
|
|
if price_float > 200000: |
|
|
price = str(round(price_float / 5000) * 5000) |
|
|
elif price_float > 30000: |
|
|
price = str(round(price_float / 1000) * 1000) |
|
|
elif price_float > 10000: |
|
|
price = str(round(price_float / 100) * 100) |
|
|
else: |
|
|
price = str(round(price_float / 10) * 10) |
|
|
except (ValueError, TypeError): |
|
|
price = price_str |
|
|
|
|
|
|
|
|
if price_eft_str: |
|
|
try: |
|
|
price_eft_float = float(price_eft_str) |
|
|
if price_eft_float > 200000: |
|
|
price_eft = str(round(price_eft_float / 5000) * 5000) |
|
|
elif price_eft_float > 30000: |
|
|
price_eft = str(round(price_eft_float / 1000) * 1000) |
|
|
elif price_eft_float > 10000: |
|
|
price_eft = str(round(price_eft_float / 100) * 100) |
|
|
else: |
|
|
price_eft = str(round(price_eft_float / 10) * 10) |
|
|
except (ValueError, TypeError): |
|
|
price_eft = price_eft_str |
|
|
else: |
|
|
try: |
|
|
price_eft_float = float(price_str) |
|
|
price_eft = str(round(price_eft_float * 0.975 / 10) * 10) |
|
|
except: |
|
|
price_eft = "" |
|
|
|
|
|
|
|
|
item_info = (stock_amount, price, product_link, price_eft, str(stock_number)) |
|
|
products.append((name, item_info, full_name)) |
|
|
|
|
|
print(f"\n📊 SONUÇ:") |
|
|
print(f" Toplam ürün: {len(all_items)}") |
|
|
print(f" Marlin ürünü: {marlin_count}") |
|
|
print(f" Products listesi: {len(products)} ürün") |
|
|
|
|
|
|
|
|
initialize_whatsapp_features(products) |
|
|
print("✅ WhatsApp enhanced features başlatıldı") |
|
|
|
|
|
if marlin_count == 0: |
|
|
print("❌ XML'de hiç Marlin ürünü bulunamadı!") |
|
|
print(" Muhtemelen XML'de sadece aksesuar/yedek parça var.") |
|
|
else: |
|
|
print("✅ Marlin ürünleri XML'de mevcut") |
|
|
|
|
|
|
|
|
marlin_products = [p for p in products if 'marlin' in p[2].lower()] |
|
|
marlin_in_stock = [p for p in marlin_products if p[1][0] == "stokta"] |
|
|
marlin_out_of_stock = [p for p in marlin_products if p[1][0] == "stokta değil"] |
|
|
|
|
|
print(f"\n📊 MARLIN STOK RAPORU:") |
|
|
print(f" 📦 Toplam Marlin modeli: {len(marlin_products)}") |
|
|
print(f" ✅ Stokta olan: {len(marlin_in_stock)}") |
|
|
print(f" ❌ Stokta olmayan: {len(marlin_out_of_stock)}") |
|
|
|
|
|
if marlin_in_stock: |
|
|
print("\n✅ STOKTA OLAN MARLIN MODELLERİ:") |
|
|
for product in marlin_in_stock: |
|
|
print(f" • {product[2]} - {product[1][1]} TL ({product[1][4]} adet)") |
|
|
|
|
|
if marlin_out_of_stock: |
|
|
print("\n❌ STOKTA OLMAYAN MARLIN MODELLERİ:") |
|
|
for product in marlin_out_of_stock: |
|
|
print(f" • {product[2]}") |
|
|
|
|
|
except Exception as e: |
|
|
print(f"❌ Ürün yükleme hatası: {e}") |
|
|
import traceback |
|
|
traceback.print_exc() |
|
|
products = [] |
|
|
|
|
|
|
|
|
|
|
|
def get_system_messages(): |
|
|
return get_prompt_content_only() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
conversation_memory = {} |
|
|
|
|
|
def get_conversation_context(phone_number): |
|
|
"""Kullanıcının sohbet geçmişini getir""" |
|
|
if phone_number not in conversation_memory: |
|
|
conversation_memory[phone_number] = { |
|
|
"messages": [], |
|
|
"current_category": None, |
|
|
"last_activity": None |
|
|
} |
|
|
return conversation_memory[phone_number] |
|
|
|
|
|
def add_to_conversation(phone_number, user_message, ai_response): |
|
|
"""Sohbet geçmişine ekle""" |
|
|
import datetime |
|
|
|
|
|
context = get_conversation_context(phone_number) |
|
|
context["last_activity"] = datetime.datetime.now() |
|
|
|
|
|
context["messages"].append({ |
|
|
"user": user_message, |
|
|
"ai": ai_response, |
|
|
"timestamp": datetime.datetime.now() |
|
|
}) |
|
|
|
|
|
|
|
|
if len(context["messages"]) > 10: |
|
|
context["messages"] = context["messages"][-10:] |
|
|
|
|
|
detect_category(phone_number, user_message, ai_response) |
|
|
|
|
|
def detect_category(phone_number, user_message, ai_response): |
|
|
"""Konuşulan kategoriyi tespit et""" |
|
|
context = get_conversation_context(phone_number) |
|
|
|
|
|
categories = { |
|
|
"marlin": ["marlin", "marlin+"], |
|
|
"madone": ["madone"], |
|
|
"emonda": ["emonda", "émonda"], |
|
|
"domane": ["domane"], |
|
|
"checkpoint": ["checkpoint"], |
|
|
"fuel": ["fuel", "fuel ex", "fuel exe"], |
|
|
"procaliber": ["procaliber"], |
|
|
"supercaliber": ["supercaliber"], |
|
|
"fx": ["fx"], |
|
|
"ds": ["ds", "dual sport"], |
|
|
"powerfly": ["powerfly"], |
|
|
"rail": ["rail"], |
|
|
"verve": ["verve"], |
|
|
"townie": ["townie"] |
|
|
} |
|
|
|
|
|
user_lower = user_message.lower() |
|
|
for category, keywords in categories.items(): |
|
|
for keyword in keywords: |
|
|
if keyword in user_lower: |
|
|
context["current_category"] = category |
|
|
print(f"🎯 Kategori tespit edildi: {category} ({phone_number})") |
|
|
return category |
|
|
|
|
|
return context.get("current_category") |
|
|
|
|
|
def build_context_messages(phone_number, current_message): |
|
|
"""Sohbet geçmişi ile sistem mesajlarını oluştur""" |
|
|
context = get_conversation_context(phone_number) |
|
|
system_messages = get_system_messages() |
|
|
|
|
|
|
|
|
if context.get("current_category"): |
|
|
category_msg = f"Kullanıcı şu anda {context['current_category'].upper()} kategorisi hakkında konuşuyor. Tüm cevaplarını bu kategori bağlamında ver. Kullanıcı yeni bir kategori belirtmediği sürece {context['current_category']} hakkında bilgi vermek istediğini varsay." |
|
|
system_messages.append({"role": "system", "content": category_msg}) |
|
|
|
|
|
|
|
|
recent_messages = context["messages"][-3:] if context["messages"] else [] |
|
|
|
|
|
all_messages = system_messages.copy() |
|
|
|
|
|
|
|
|
for msg in recent_messages: |
|
|
all_messages.append({"role": "user", "content": msg["user"]}) |
|
|
all_messages.append({"role": "assistant", "content": msg["ai"]}) |
|
|
|
|
|
|
|
|
all_messages.append({"role": "user", "content": current_message}) |
|
|
|
|
|
return all_messages |
|
|
|
|
|
def process_whatsapp_message_with_memory(user_message, phone_number): |
|
|
"""Hafızalı WhatsApp mesaj işleme""" |
|
|
try: |
|
|
|
|
|
profile_analysis = analyze_user_message(phone_number, user_message) |
|
|
logger.info(f"📊 Profil analizi: {phone_number} -> {profile_analysis}") |
|
|
|
|
|
|
|
|
if any(keyword in user_message.lower() for keyword in ["öneri", "öner", "tavsiye", "ne önerirsin"]): |
|
|
personalized = get_personalized_recommendations(phone_number, products) |
|
|
if personalized.get("personalized") and personalized.get("recommendations"): |
|
|
|
|
|
profile_summary = get_user_profile_summary(phone_number) |
|
|
custom_response = create_personalized_response(personalized, profile_summary) |
|
|
return extract_product_info_whatsapp(custom_response) |
|
|
|
|
|
|
|
|
comparison_result = handle_whatsapp_comparison_request(user_message) |
|
|
if comparison_result: |
|
|
return extract_product_info_whatsapp(comparison_result) |
|
|
|
|
|
budget_result = handle_whatsapp_budget_request(user_message) |
|
|
if budget_result: |
|
|
return extract_product_info_whatsapp(budget_result) |
|
|
|
|
|
category_result = handle_whatsapp_category_request(user_message, phone_number) |
|
|
if category_result: |
|
|
return extract_product_info_whatsapp(category_result) |
|
|
|
|
|
|
|
|
messages = build_context_messages(phone_number, user_message) |
|
|
|
|
|
|
|
|
profile_summary = get_user_profile_summary(phone_number) |
|
|
if profile_summary.get("exists") and profile_summary.get("confidence", 0) > 0.3: |
|
|
profile_context = create_profile_context_message(profile_summary) |
|
|
messages.append({"role": "system", "content": profile_context}) |
|
|
|
|
|
|
|
|
input_words = user_message.lower().split() |
|
|
for product_info in products: |
|
|
if product_info[0] in input_words: |
|
|
if product_info[1][0] == "stokta": |
|
|
normal_price = f"Fiyat: {product_info[1][1]} TL" |
|
|
if product_info[1][3]: |
|
|
eft_price = f"Havale: {product_info[1][3]} TL" |
|
|
price_info = f"{normal_price}, {eft_price}" |
|
|
else: |
|
|
price_info = normal_price |
|
|
|
|
|
new_msg = f"{product_info[2]} {product_info[1][0]} - {price_info}" |
|
|
else: |
|
|
new_msg = f"{product_info[2]} {product_info[1][0]}" |
|
|
messages.append({"role": "system", "content": new_msg}) |
|
|
|
|
|
if not OPENAI_API_KEY: |
|
|
return "OpenAI API anahtarı eksik. Lütfen environment variables'ları kontrol edin." |
|
|
|
|
|
payload = { |
|
|
"model": "gpt-4.1", |
|
|
"messages": messages, |
|
|
"temperature": 0.3, |
|
|
"max_tokens": 800, |
|
|
"stream": False, |
|
|
} |
|
|
|
|
|
headers = { |
|
|
"Content-Type": "application/json", |
|
|
"Authorization": f"Bearer {OPENAI_API_KEY}" |
|
|
} |
|
|
|
|
|
response = requests.post(API_URL, headers=headers, json=payload) |
|
|
if response.status_code == 200: |
|
|
result = response.json() |
|
|
ai_response = result['choices'][0]['message']['content'] |
|
|
|
|
|
|
|
|
formatted_response = extract_product_info_whatsapp(ai_response) |
|
|
|
|
|
|
|
|
add_to_conversation(phone_number, user_message, formatted_response) |
|
|
|
|
|
return formatted_response |
|
|
else: |
|
|
print(f"OpenAI API Error: {response.status_code} - {response.text}") |
|
|
return f"API hatası: {response.status_code}. Lütfen daha sonra tekrar deneyin." |
|
|
|
|
|
except Exception as e: |
|
|
print(f"❌ WhatsApp mesaj işleme hatası: {e}") |
|
|
return "Teknik bir sorun oluştu. Lütfen daha sonra tekrar deneyin." |
|
|
|
|
|
def create_profile_context_message(profile_summary): |
|
|
"""Profil bilgilerini sistem mesajına çevir""" |
|
|
context_parts = [] |
|
|
|
|
|
preferences = profile_summary.get("preferences", {}) |
|
|
behavior = profile_summary.get("behavior", {}) |
|
|
|
|
|
|
|
|
if preferences.get("budget_min") and preferences.get("budget_max"): |
|
|
budget_min = preferences["budget_min"] |
|
|
budget_max = preferences["budget_max"] |
|
|
context_parts.append(f"Kullanıcının bütçesi: {budget_min:,}-{budget_max:,} TL") |
|
|
|
|
|
|
|
|
if preferences.get("categories"): |
|
|
categories = ", ".join(preferences["categories"]) |
|
|
context_parts.append(f"İlgilendiği kategoriler: {categories}") |
|
|
|
|
|
|
|
|
if preferences.get("usage_purpose"): |
|
|
purposes = ", ".join(preferences["usage_purpose"]) |
|
|
context_parts.append(f"Kullanım amacı: {purposes}") |
|
|
|
|
|
|
|
|
if behavior.get("price_sensitive"): |
|
|
context_parts.append("Fiyata duyarlı bir kullanıcı") |
|
|
if behavior.get("tech_interested"): |
|
|
context_parts.append("Teknik detaylarla ilgilenen bir kullanıcı") |
|
|
if behavior.get("comparison_lover"): |
|
|
context_parts.append("Karşılaştırma yapmayı seven bir kullanıcı") |
|
|
|
|
|
|
|
|
interaction_style = profile_summary.get("interaction_style", "balanced") |
|
|
style_descriptions = { |
|
|
"analytical": "Detaylı ve analitik bilgi bekleyen", |
|
|
"budget_conscious": "Bütçe odaklı ve ekonomik seçenekleri arayan", |
|
|
"technical": "Teknik özellikler ve spesifikasyonlarla ilgilenen", |
|
|
"decisive": "Hızlı karar veren ve özet bilgi isteyen", |
|
|
"balanced": "Dengeli yaklaşım sergileyen" |
|
|
} |
|
|
context_parts.append(f"{style_descriptions.get(interaction_style, 'balanced')} bir kullanıcı") |
|
|
|
|
|
if context_parts: |
|
|
return f"Kullanıcı profili: {'. '.join(context_parts)}. Bu bilgileri göz önünde bulundurarak cevap ver." |
|
|
return "" |
|
|
|
|
|
def create_personalized_response(personalized_data, profile_summary): |
|
|
"""Kişiselleştirilmiş öneri cevabı oluştur""" |
|
|
response_parts = [] |
|
|
|
|
|
|
|
|
interaction_style = profile_summary.get("interaction_style", "balanced") |
|
|
if interaction_style == "analytical": |
|
|
response_parts.append("🔍 Profilinizi analiz ederek sizin için en uygun seçenekleri belirledim:") |
|
|
elif interaction_style == "budget_conscious": |
|
|
response_parts.append("💰 Bütçenize uygun en iyi seçenekleri hazırladım:") |
|
|
elif interaction_style == "technical": |
|
|
response_parts.append("⚙️ Teknik tercihlerinize göre önerilerim:") |
|
|
else: |
|
|
response_parts.append("🎯 Size özel seçtiklerim:") |
|
|
|
|
|
|
|
|
recommendations = personalized_data.get("recommendations", [])[:3] |
|
|
|
|
|
if recommendations: |
|
|
response_parts.append("\n") |
|
|
for i, product in enumerate(recommendations, 1): |
|
|
name, item_info, full_name = product |
|
|
price = item_info[1] if len(item_info) > 1 else "Fiyat yok" |
|
|
response_parts.append(f"**{i}. {full_name}**") |
|
|
response_parts.append(f"💰 Fiyat: {price} TL") |
|
|
response_parts.append("") |
|
|
|
|
|
|
|
|
preferences = profile_summary.get("preferences", {}) |
|
|
if preferences.get("categories"): |
|
|
category = preferences["categories"][0] |
|
|
response_parts.append(f"Bu öneriler {category} kategorisindeki ilginizi ve tercihlerinizi dikkate alarak hazırlandı.") |
|
|
|
|
|
return "\n".join(response_parts) |
|
|
|
|
|
def split_long_message(message, max_length=1600): |
|
|
"""Uzun mesajları WhatsApp için uygun parçalara böler""" |
|
|
if len(message) <= max_length: |
|
|
return [message] |
|
|
|
|
|
parts = [] |
|
|
remaining = message |
|
|
|
|
|
while len(remaining) > max_length: |
|
|
cut_point = max_length |
|
|
|
|
|
|
|
|
for i in range(max_length, max_length - 200, -1): |
|
|
if i < len(remaining) and remaining[i] in ['.', '!', '?', '\n']: |
|
|
cut_point = i + 1 |
|
|
break |
|
|
elif i < len(remaining) and remaining[i] in [' ', ',', ';']: |
|
|
cut_point = i |
|
|
|
|
|
parts.append(remaining[:cut_point].strip()) |
|
|
remaining = remaining[cut_point:].strip() |
|
|
|
|
|
if remaining: |
|
|
parts.append(remaining) |
|
|
|
|
|
return parts |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_whatsapp_message(user_message): |
|
|
try: |
|
|
system_messages = get_system_messages() |
|
|
|
|
|
|
|
|
input_words = user_message.lower().split() |
|
|
for product_info in products: |
|
|
if product_info[0] in input_words: |
|
|
if product_info[1][0] == "stokta": |
|
|
normal_price = f"Fiyat: {product_info[1][1]} TL" |
|
|
if product_info[1][3]: |
|
|
eft_price = f"Havale: {product_info[1][3]} TL" |
|
|
price_info = f"{normal_price}, {eft_price}" |
|
|
else: |
|
|
price_info = normal_price |
|
|
|
|
|
new_msg = f"{product_info[2]} {product_info[1][0]} - {price_info}" |
|
|
else: |
|
|
new_msg = f"{product_info[2]} {product_info[1][0]}" |
|
|
system_messages.append({"role": "system", "content": new_msg}) |
|
|
|
|
|
messages = system_messages + [{"role": "user", "content": user_message}] |
|
|
|
|
|
if not OPENAI_API_KEY: |
|
|
return "OpenAI API anahtarı eksik. Lütfen environment variables'ları kontrol edin." |
|
|
|
|
|
payload = { |
|
|
"model": "gpt-4.1", |
|
|
"messages": messages, |
|
|
"temperature": 0.3, |
|
|
"max_tokens": 800, |
|
|
"stream": False, |
|
|
} |
|
|
|
|
|
headers = { |
|
|
"Content-Type": "application/json", |
|
|
"Authorization": f"Bearer {OPENAI_API_KEY}" |
|
|
} |
|
|
|
|
|
response = requests.post(API_URL, headers=headers, json=payload) |
|
|
if response.status_code == 200: |
|
|
result = response.json() |
|
|
return result['choices'][0]['message']['content'] |
|
|
else: |
|
|
print(f"OpenAI API Error: {response.status_code} - {response.text}") |
|
|
return f"API hatası: {response.status_code}. Lütfen daha sonra tekrar deneyin." |
|
|
|
|
|
except Exception as e: |
|
|
print(f"❌ WhatsApp mesaj işleme hatası: {e}") |
|
|
return "Teknik bir sorun oluştu. Lütfen daha sonra tekrar deneyin." |
|
|
|
|
|
|
|
|
app = FastAPI() |
|
|
|
|
|
@app.post("/whatsapp-webhook") |
|
|
async def whatsapp_webhook(request: Request): |
|
|
try: |
|
|
form_data = await request.form() |
|
|
|
|
|
from_number = form_data.get('From') |
|
|
to_number = form_data.get('To') |
|
|
message_body = form_data.get('Body') |
|
|
message_status = form_data.get('MessageStatus') |
|
|
|
|
|
print(f"📱 Webhook - From: {from_number}, Body: {message_body}, Status: {message_status}") |
|
|
|
|
|
|
|
|
if message_status in ['sent', 'delivered', 'read', 'failed']: |
|
|
return {"status": "ignored", "message": f"Status: {message_status}"} |
|
|
|
|
|
|
|
|
if to_number != TWILIO_WHATSAPP_NUMBER: |
|
|
return {"status": "ignored", "message": "Outgoing message"} |
|
|
|
|
|
|
|
|
if not message_body or message_body.strip() == "": |
|
|
return {"status": "ignored", "message": "Empty message"} |
|
|
|
|
|
print(f"✅ MESAJ ALINDI: {from_number} -> {message_body}") |
|
|
|
|
|
if not twilio_client: |
|
|
return {"status": "error", "message": "Twilio yapılandırması eksik"} |
|
|
|
|
|
|
|
|
ai_response = process_whatsapp_message_with_memory(message_body, from_number) |
|
|
|
|
|
|
|
|
message_parts = split_long_message(ai_response, max_length=1600) |
|
|
|
|
|
sent_messages = [] |
|
|
|
|
|
|
|
|
for i, part in enumerate(message_parts): |
|
|
if len(message_parts) > 1: |
|
|
if i == 0: |
|
|
part = f"{part}\n\n(1/{len(message_parts)})" |
|
|
elif i == len(message_parts) - 1: |
|
|
part = f"({i+1}/{len(message_parts)})\n\n{part}" |
|
|
else: |
|
|
part = f"({i+1}/{len(message_parts)})\n\n{part}" |
|
|
|
|
|
|
|
|
message = twilio_client.messages.create( |
|
|
messaging_service_sid=TWILIO_MESSAGING_SERVICE_SID, |
|
|
body=part, |
|
|
to=from_number |
|
|
) |
|
|
|
|
|
sent_messages.append(message.sid) |
|
|
|
|
|
if i < len(message_parts) - 1: |
|
|
import time |
|
|
time.sleep(0.5) |
|
|
|
|
|
print(f"✅ {len(message_parts)} PARÇA GÖNDERİLDİ") |
|
|
|
|
|
|
|
|
context = get_conversation_context(from_number) |
|
|
if context.get("current_category"): |
|
|
print(f"💭 Aktif kategori: {context['current_category']}") |
|
|
|
|
|
return { |
|
|
"status": "success", |
|
|
"message_parts": len(message_parts), |
|
|
"message_sids": sent_messages, |
|
|
"current_category": context.get("current_category") |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
print(f"❌ Webhook hatası: {str(e)}") |
|
|
return {"status": "error", "message": str(e)} |
|
|
|
|
|
@app.get("/") |
|
|
async def root(): |
|
|
return {"message": "Trek WhatsApp Bot çalışıyor!", "status": "active"} |
|
|
|
|
|
|
|
|
@app.get("/clear-memory/{phone_number}") |
|
|
async def clear_memory(phone_number: str): |
|
|
"""Belirli bir telefon numarasının hafızasını temizle""" |
|
|
if phone_number in conversation_memory: |
|
|
del conversation_memory[phone_number] |
|
|
return {"status": "success", "message": f"{phone_number} hafızası temizlendi"} |
|
|
return {"status": "info", "message": "Hafıza bulunamadı"} |
|
|
|
|
|
|
|
|
@app.get("/debug-memory") |
|
|
async def debug_memory(): |
|
|
"""Tüm hafızayı görüntüle (debug için)""" |
|
|
memory_info = {} |
|
|
for phone, context in conversation_memory.items(): |
|
|
memory_info[phone] = { |
|
|
"current_category": context.get("current_category"), |
|
|
"message_count": len(context.get("messages", [])), |
|
|
"last_activity": str(context.get("last_activity")) |
|
|
} |
|
|
return {"conversation_memory": memory_info} |
|
|
|
|
|
|
|
|
@app.get("/debug-profile/{phone_number}") |
|
|
async def debug_profile(phone_number: str): |
|
|
"""Belirli kullanıcının profil bilgilerini görüntüle""" |
|
|
profile_summary = get_user_profile_summary(phone_number) |
|
|
return {"phone_number": phone_number, "profile": profile_summary} |
|
|
|
|
|
|
|
|
@app.get("/debug-profiles") |
|
|
async def debug_profiles(): |
|
|
"""Tüm kullanıcı profillerini görüntüle""" |
|
|
from whatsapp_passive_profiler import passive_profiler |
|
|
all_profiles = {} |
|
|
for phone_number in passive_profiler.profiles.keys(): |
|
|
all_profiles[phone_number] = get_user_profile_summary(phone_number) |
|
|
return {"profiles": all_profiles} |
|
|
|
|
|
@app.get("/health") |
|
|
async def health(): |
|
|
return { |
|
|
"status": "healthy", |
|
|
"twilio_configured": twilio_client is not None, |
|
|
"openai_configured": OPENAI_API_KEY is not None, |
|
|
"products_loaded": len(products), |
|
|
"webhook_endpoint": "/whatsapp-webhook" |
|
|
} |
|
|
|
|
|
if __name__ == "__main__": |
|
|
import uvicorn |
|
|
print("🚀 Trek WhatsApp Bot başlatılıyor...") |
|
|
uvicorn.run(app, host="0.0.0.0", port=7860) |