|
|
|
|
|
|
|
""" |
|
WhatsApp Mağaza Bildirim Sistemi |
|
Müşteri taleplerini mağazalara WhatsApp ile bildirir |
|
""" |
|
|
|
import os |
|
import logging |
|
from datetime import datetime |
|
from twilio.rest import Client |
|
from typing import Optional, Dict |
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
STORE_NUMBERS = { |
|
"caddebostan": "+905439362335", |
|
"ortakoy": "+905439362335", |
|
"sariyer": "+905439362335", |
|
"alsancak": "+905439362335", |
|
"merkez": "+905439362335" |
|
} |
|
|
|
|
|
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") |
|
|
|
def send_store_notification( |
|
customer_phone: str, |
|
customer_name: Optional[str], |
|
product_name: str, |
|
action: str, |
|
store_name: Optional[str] = None, |
|
additional_info: Optional[str] = None |
|
) -> bool: |
|
""" |
|
Mağazaya WhatsApp bildirimi gönder |
|
|
|
Args: |
|
customer_phone: Müşteri telefon numarası |
|
customer_name: Müşteri adı (opsiyonel) |
|
product_name: Ürün adı |
|
action: İşlem tipi (ayırtma, bilgi, fiyat, stok) |
|
store_name: Hangi mağazaya bildirim gidecek |
|
additional_info: Ek bilgi |
|
|
|
Returns: |
|
Başarılı ise True |
|
""" |
|
|
|
try: |
|
|
|
message = format_notification_message( |
|
customer_phone, |
|
customer_name, |
|
product_name, |
|
action, |
|
store_name, |
|
additional_info |
|
) |
|
|
|
|
|
import json |
|
from datetime import datetime |
|
notification_log = { |
|
"timestamp": datetime.now().isoformat(), |
|
"customer_phone": customer_phone, |
|
"customer_name": customer_name, |
|
"product_name": product_name, |
|
"action": action, |
|
"store_name": store_name, |
|
"additional_info": additional_info, |
|
"formatted_message": message |
|
} |
|
|
|
|
|
log_file = "mehmet_bey_notifications.json" |
|
try: |
|
with open(log_file, "r") as f: |
|
logs = json.load(f) |
|
except: |
|
logs = [] |
|
|
|
logs.append(notification_log) |
|
|
|
|
|
if len(logs) > 100: |
|
logs = logs[-100:] |
|
|
|
with open(log_file, "w") as f: |
|
json.dump(logs, f, indent=2, ensure_ascii=False) |
|
|
|
logger.info(f"📝 Bildirim kaydedildi: {log_file}") |
|
logger.info(f" Action: {action}, Product: {product_name}") |
|
|
|
|
|
if not TWILIO_ACCOUNT_SID or not TWILIO_AUTH_TOKEN: |
|
logger.warning("⚠️ Twilio credentials missing - notification saved to file only") |
|
return True |
|
|
|
client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN) |
|
|
|
|
|
if store_name and store_name.lower() in STORE_NUMBERS: |
|
target_number = STORE_NUMBERS[store_name.lower()] |
|
else: |
|
target_number = STORE_NUMBERS["merkez"] |
|
|
|
|
|
message = format_notification_message( |
|
customer_phone, |
|
customer_name, |
|
product_name, |
|
action, |
|
store_name, |
|
additional_info |
|
) |
|
|
|
|
|
whatsapp_number = f"whatsapp:{target_number}" |
|
|
|
|
|
from_number = "whatsapp:+905332047254" |
|
|
|
try: |
|
|
|
msg = client.messages.create( |
|
from_=from_number, |
|
body=message, |
|
to=whatsapp_number, |
|
|
|
) |
|
except Exception as twilio_error: |
|
|
|
logger.error(f"Twilio API hatası: {twilio_error}") |
|
if "21609" in str(twilio_error): |
|
logger.info("Not: Hedef numara henüz Sandbox'a katılmamış olabilir") |
|
return True |
|
|
|
logger.info(f"✅ Mağaza bildirimi gönderildi: {msg.sid}") |
|
logger.info(f" Hedef: {whatsapp_number}") |
|
logger.info(f" İşlem: {action}") |
|
|
|
return True |
|
|
|
except Exception as e: |
|
logger.error(f"❌ Mağaza bildirim hatası: {e}") |
|
return False |
|
|
|
def format_notification_message( |
|
customer_phone: str, |
|
customer_name: Optional[str], |
|
product_name: str, |
|
action: str, |
|
store_name: Optional[str], |
|
additional_info: Optional[str] |
|
) -> str: |
|
""" |
|
Bildirim mesajını formatla |
|
""" |
|
|
|
|
|
now = datetime.now() |
|
date_str = now.strftime("%d.%m.%Y %H:%M") |
|
|
|
|
|
action_config = { |
|
"reserve": { |
|
"emoji": "🔔", |
|
"title": "YENİ AYIRTMA TALEBİ" |
|
}, |
|
"info": { |
|
"emoji": "ℹ️", |
|
"title": "BİLGİ TALEBİ" |
|
}, |
|
"price": { |
|
"emoji": "💰", |
|
"title": "FİYAT SORUSU" |
|
}, |
|
"stock": { |
|
"emoji": "📦", |
|
"title": "STOK SORUSU" |
|
}, |
|
"test": { |
|
"emoji": "🧪", |
|
"title": "TEST DENEMESİ" |
|
} |
|
} |
|
|
|
config = action_config.get(action, action_config["info"]) |
|
|
|
|
|
message_parts = [ |
|
f"{config['emoji']} *{config['title']}*", |
|
f"📅 {date_str}", |
|
"", |
|
f"👤 *Müşteri:*" |
|
] |
|
|
|
if customer_name: |
|
message_parts.append(f" İsim: {customer_name}") |
|
|
|
|
|
phone_display = customer_phone.replace("whatsapp:", "") |
|
message_parts.append(f" Tel: {phone_display}") |
|
|
|
message_parts.extend([ |
|
"", |
|
f"🚲 *Ürün:* {product_name}" |
|
]) |
|
|
|
if store_name: |
|
message_parts.append(f"🏪 *Mağaza:* {store_name.title()}") |
|
|
|
if additional_info: |
|
message_parts.extend([ |
|
"", |
|
f"📝 *Not:*", |
|
additional_info |
|
]) |
|
|
|
|
|
if action == "reserve": |
|
message_parts.extend([ |
|
"", |
|
"✅ *Yapılacaklar:*", |
|
"1. Ürün stok kontrolü", |
|
"2. Müşteriyi arayın", |
|
"3. Ödeme/teslimat planı" |
|
]) |
|
elif action == "price": |
|
message_parts.extend([ |
|
"", |
|
"💡 *Müşteriye güncel fiyat bildirin*" |
|
]) |
|
elif action == "stock": |
|
message_parts.extend([ |
|
"", |
|
"💡 *Stok durumunu kontrol edip bildirin*" |
|
]) |
|
|
|
message_parts.extend([ |
|
"", |
|
"---", |
|
"Trek WhatsApp Bot", |
|
"📍 Alsancak Mağaza - Mehmet Bey" |
|
]) |
|
|
|
return "\n".join(message_parts) |
|
|
|
def send_test_notification() -> bool: |
|
""" |
|
Test bildirimi gönder |
|
""" |
|
return send_store_notification( |
|
customer_phone="whatsapp:+905551234567", |
|
customer_name="Test Müşteri", |
|
product_name="FX 2 (Kırmızı, L Beden)", |
|
action="test", |
|
store_name="merkez", |
|
additional_info="Bu bir test bildirimidir. Sistem çalışıyor." |
|
) |
|
|
|
|
|
def notify_product_reservation(customer_phone: str, product_name: str, store: Optional[str] = None): |
|
"""Ürün ayırtma bildirimi""" |
|
return send_store_notification( |
|
customer_phone=customer_phone, |
|
customer_name=None, |
|
product_name=product_name, |
|
action="reserve", |
|
store_name=store, |
|
additional_info="Müşteri bu ürünü ayırtmak istiyor. Lütfen iletişime geçin." |
|
) |
|
|
|
def notify_price_inquiry(customer_phone: str, product_name: str): |
|
"""Fiyat sorusu bildirimi""" |
|
return send_store_notification( |
|
customer_phone=customer_phone, |
|
customer_name=None, |
|
product_name=product_name, |
|
action="price", |
|
additional_info="Müşteri güncel fiyat bilgisi istiyor." |
|
) |
|
|
|
def notify_stock_inquiry(customer_phone: str, product_name: str, store: Optional[str] = None): |
|
"""Stok sorusu bildirimi""" |
|
return send_store_notification( |
|
customer_phone=customer_phone, |
|
customer_name=None, |
|
product_name=product_name, |
|
action="stock", |
|
store_name=store, |
|
additional_info="Müşteri stok durumunu soruyor." |
|
) |
|
|
|
def should_notify_mehmet_bey(user_message: str, intent_analysis: Optional[Dict] = None) -> tuple[bool, str, str]: |
|
""" |
|
Mehmet Bey'e bildirim gönderilmeli mi kontrol et |
|
|
|
Args: |
|
user_message: Müşteri mesajı |
|
intent_analysis: GPT-5 intent analiz sonucu (opsiyonel) |
|
|
|
Returns: |
|
(should_notify: bool, reason: str, urgency: "high"/"medium"/"low") |
|
""" |
|
|
|
message_lower = user_message.lower() |
|
|
|
|
|
reservation_keywords = [ |
|
|
|
'ayırt', 'ayırın', 'ayırtın', 'ayırtabilir', 'ayırır mısınız', |
|
'rezerve', 'rezervasyon', 'rezarvasyon', |
|
'tutun', 'tutar mısınız', 'tutabilir', 'tutarsanız', 'tuttun mu', |
|
'sakla', 'saklar mısınız', 'saklayın', 'saklayabilir', |
|
'kenara koy', 'kenara ayır', 'benim için koy', 'bana ayır', |
|
|
|
|
|
'alacağım', 'alıcam', 'alıyorum', 'alcam', 'alırım', |
|
'geliyorum', 'gelcem', 'gelicem', 'geliyom', 'geleceğim', |
|
'yola çıktım', 'yoldayım', 'yola çıkıyorum', 'yola çıkıcam', |
|
'birazdan oradayım', 'yarım saate', '1 saate', 'bir saate', |
|
'30 dakika', '45 dakika', 'akşama kadar', |
|
|
|
|
|
'bugün alabilir', 'yarın alabilir', 'hafta sonu', 'haftasonu', |
|
'akşam uğra', 'öğleden sonra gel', 'sabah gel', 'öğlen gel', |
|
'eşime danış', 'eşimle konuş', 'eşimi getir', 'eşimle gel', |
|
'kesin alıcıyım', 'kesin istiyorum', 'bunu istiyorum', 'bunu alıyorum', |
|
'kartımı unuttum', 'nakit getir', 'para çek', 'atm', |
|
'maaş yattı', 'maaşım yatınca', 'ay başı', 'aybaşı' |
|
] |
|
|
|
|
|
store_keywords = [ |
|
'hangi mağaza', 'nerede var', 'nereden alabilirim', 'nerden', |
|
'caddebostan', 'ortaköy', 'ortakoy', 'alsancak', 'bahçeköy', 'bahcekoy', |
|
'en yakın', 'bana yakın', 'yakın mağaza', 'yakınımda', |
|
'mağazada görebilir', 'mağazaya gel', 'mağazanıza', 'mağazanızda', |
|
'test edebilir', 'deneyebilir', 'binebilir', 'test sürüş', |
|
'yerinde gör', 'canlı gör', 'fiziksel', 'gerçeğini gör', |
|
'adres', 'konum', 'lokasyon', 'neredesiniz', 'harita', |
|
'uzak mı', 'yakın mı', 'kaç km', 'nasıl gidilir' |
|
] |
|
|
|
|
|
price_keywords = [ |
|
'indirim', 'kampanya', 'promosyon', 'fırsat', 'özel fiyat', |
|
'taksit', 'kredi kartı', 'banka', 'ödeme seçenek', 'vadeli', |
|
'peşin öde', 'nakit indirim', 'peşinat', 'kaparo', 'depozito', |
|
'pazarlık', 'son fiyat', 'en iyi fiyat', 'net fiyat', 'kdv dahil', |
|
'öğrenci indirimi', 'personel indirimi', 'kurumsal', 'toplu alım', |
|
'takas', 'eski bisiklet', 'değer', '2.el', 'ikinci el', |
|
'daha ucuz', 'daha uygun', 'bütçe', 'pahalı', 'ucuzlat', |
|
'fiyat düş', 'fiyat kır', 'esnet', 'yardımcı ol' |
|
] |
|
|
|
|
|
for keyword in reservation_keywords: |
|
if keyword in message_lower: |
|
return True, f"🔴 Rezervasyon talebi: '{keyword}' kelimesi tespit edildi", "high" |
|
|
|
|
|
for keyword in store_keywords: |
|
if keyword in message_lower: |
|
return True, f"📍 Mağaza/lokasyon sorusu: '{keyword}' kelimesi tespit edildi", "medium" |
|
|
|
|
|
for keyword in price_keywords: |
|
if keyword in message_lower: |
|
return True, f"💰 Fiyat/ödeme görüşmesi: '{keyword}' kelimesi tespit edildi", "medium" |
|
|
|
|
|
if intent_analysis: |
|
|
|
if intent_analysis.get('urgency') == 'high': |
|
return True, "🚨 GPT-5 yüksek aciliyet tespit etti", "high" |
|
|
|
|
|
if 'reserve' in intent_analysis.get('intents', []): |
|
return True, "📌 GPT-5 rezervasyon niyeti tespit etti", "high" |
|
|
|
|
|
return False, "", "low" |
|
|
|
if __name__ == "__main__": |
|
|
|
print("Mağaza bildirim sistemi test ediliyor...") |
|
result = send_test_notification() |
|
if result: |
|
print("✅ Test bildirimi başarıyla gönderildi!") |
|
else: |
|
print("❌ Test bildirimi gönderilemedi!") |