Update app.py
Browse files
app.py
CHANGED
@@ -17,11 +17,6 @@ from googleapiclient.http import MediaIoBaseDownload
|
|
17 |
import io
|
18 |
import warnings
|
19 |
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
20 |
-
|
21 |
-
# WhatsApp için yeni import'lar
|
22 |
-
from fastapi import FastAPI, Request, Form
|
23 |
-
from twilio.rest import Client
|
24 |
-
|
25 |
warnings.simplefilter('ignore', InsecureRequestWarning)
|
26 |
|
27 |
# Gradio uyarılarını bastır
|
@@ -37,22 +32,6 @@ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
|
37 |
if not OPENAI_API_KEY:
|
38 |
print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")
|
39 |
|
40 |
-
# Twilio WhatsApp ayarları
|
41 |
-
TWILIO_ACCOUNT_SID = os.getenv("TWILIO_ACCOUNT_SID")
|
42 |
-
TWILIO_AUTH_TOKEN = os.getenv("TWILIO_AUTH_TOKEN")
|
43 |
-
TWILIO_WHATSAPP_NUMBER = "whatsapp:+14155238886"
|
44 |
-
|
45 |
-
if not TWILIO_ACCOUNT_SID or not TWILIO_AUTH_TOKEN:
|
46 |
-
print("Uyarı: Twilio bilgileri eksik! WhatsApp özelliği çalışmayacak.")
|
47 |
-
twilio_client = None
|
48 |
-
else:
|
49 |
-
try:
|
50 |
-
twilio_client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
|
51 |
-
print("Twilio client başarıyla oluşturuldu!")
|
52 |
-
except Exception as e:
|
53 |
-
print(f"Twilio client oluşturma hatası: {e}")
|
54 |
-
twilio_client = None
|
55 |
-
|
56 |
# Trek bisiklet ürünlerini çekme
|
57 |
url = 'https://www.trekbisiklet.com.tr/output/8582384479'
|
58 |
response = requests.get(url, verify=False)
|
@@ -60,7 +39,7 @@ root = ET.fromstring(response.content)
|
|
60 |
|
61 |
products = []
|
62 |
for item in root.findall('item'):
|
63 |
-
|
64 |
name_words = item.find('rootlabel').text.lower().split()
|
65 |
name = name_words[0]
|
66 |
full_name = ' '.join(name_words)
|
@@ -181,7 +160,7 @@ for item in root.findall('item'):
|
|
181 |
# Sadece ürün linkini al, resim linkini alma
|
182 |
product_link = item.find('productLink').text if item.find('productLink') is not None else ""
|
183 |
|
184 |
-
|
185 |
item_info = (stock_amount, price, product_link, price_eft, price_rebate, price_rebate_money_order)
|
186 |
else:
|
187 |
# Stokta olmayan ürünler için sadece stok durumunu belirt, fiyat ve link bilgisi verme
|
@@ -310,9 +289,20 @@ def run_scheduler(chat_history_ref):
|
|
310 |
schedule.run_pending()
|
311 |
time.sleep(60)
|
312 |
|
313 |
-
|
314 |
-
def
|
315 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
316 |
system_messages = [
|
317 |
{"role": "system", "content": "Bir önceki sohbeti unut. Vereceğin ürün bilgisi, bu bilginin içinde yan yana yazmıyorsa veya arada başka bilgiler yazıyor ise, o bilgiyi vermeyeceksin çünkü o bilgi yanlıştır. vereceğin bilgiyi bu bilgilerin içinden alıyorsan her kelimenin yan yana yazmazı şartı ile o bilgiyi verebilirsin. Madone SLR bisikletler soruluyorsa (GEN 7) ibaresini kendin ekleyerek, aramayı GEN 7'li yap.Sana verilen bilgilerin içinde bir ürün adı veya bisiklet modelinin rengi yoksa, ürün ile ilgili bilgi vermeyeceksin ve sorulan modelden farklı boy ve renkler stoklarda varsa, bu bilgileri vereceksin. Alternatif renk veya boyu yok ise, başka bir model adını öğrenirsen stokları tekrar kontrol edebileceğini söyleyeceksin. Sana bir model adı rakamı ile verilmiş ve bu ürün bu bilgiler içinde yok ise, o ürün stoklarımızda yoktur diye bilgi vereceksin ve model adı rakamsız girilmiş ise nodel adının rakamı ile girilmesini rica edeceksin, örnek olarak 'Madone SL 7' gibi 7 rakamının da yazılmasını rica edeceksin. Madone, Emonda, Domane ve Checpont modelleri birer yol bisikleti modelidir, bu modellerin renklerinden önce yazan ve 47, 49, 50, 52, 54, 56, 58, 60, 62, 64 rakamları, o bisikletlerin boylarıdır. Bu bilgi içindeki renkler ise o ürünlerin renkleridir. Sana bir ürün var mı diye sorulduğunda, sadece bilgi içinde olan ürünleri söyleyebilirsin. Stoklarımızda yok ise o ürün ile ilgili bilgi vermeyeceksin. En büyük veya en küçük boy sorulduğunda, bilgi içinde renki bilgisi olan modellerin bilgisini vereceksin. Gerçek zamanlı stok bilgilerine erişme yeteneğin var. En aşağıdaki ürünlerin adına, rengine, boyuna ve fiyatına tam erişimin var ve bunları bilmiyorum demeyeceksin. Üyelere özel fiyatları ve indirimleri görmek için kullanıcıların siteye üye olmaları gerekmektedir. Sen bir AI Trek marka bisiklet uzmanı, bilir kişisi ve asistanısın.Trek ve Electra bisikletler konusunda uzmanım.4cü şubemizi İzmirde açtık.İzmir adresi: Sezer Doğan Sok. The Kar Suits 14A Alsancak Konak İzmir. İzmir şubesinin telefon numarası:0543 936 2335 . İstanbul'da üç Trek mağazamız var: Caddebostan, Ortaköy ve Sarıyer. Ortaköy mağazası 10.00-19.00 saatleri arasında açık. ve Toyota Plaza ve Carrefour'un yanindadir,tam adresi Dereboyu Cad No:84 Ortaköy Beşiktaş ve telefon numarası 0212 2271015. Caddebostan mağazası, Prof. Dr. Hulusi Behçet 18 Caddebostan, Kadıköy adresinde, Göztepe Parkı karşısındadır, telefon numarası 0216 6292432, 10.00-19.00 saatleri arasında açık. Tüm mağazalar Pazar günü kapalıdır. Caddebostan mağazamızda haftanın her günü Bike fit yapılmaktadır ve ücreti 3500 TL ve süresi 60-90 dakika. Bike fit yaptırmak isteyenler, Bike fit sayfamızda sağ tarafta bulunan RANDEVU AL butonu ile randevu oluştumaları gerekmektedir. Sarıyer mağazamızın adresi şöyledir: Mareşal Fevzi Çakmak Cad. No 54 Kemer-Bahçeköy Mahallsi Sarıyer, hafta içleri ve cumartesi günleri 10.00 ile 19.00 saatleri arasında hizmet vermektedir. Bu mağazamız elektrikli bisikletlerin daha çok sergilendiği ve tüm çeşiti bir arada görebileceğiniz mağazamızdır. Maslaktan, Belgrad ormanına gelirken sol tarafta kalmaktadır ve telefon numarası 0542 137 1080.."},
|
318 |
{"role": "system", "content": "Dağ bisikletleri modelleri: Marlin, Roscoe, Procaliber, Supercaliber, Fuel Ex. Şehir bisikletleri: FX ve DS (Dual Sport). Elektrikli Bisiklet modelleri: Powerfly, Powerfly FS, Rail, Fuel Exe, Domane SLR +, Verve +, Townie +, Fx +, DS +. Dağ bisikletlerinin boyları XXS, XS, S, M, ML, L, XL'dir. Canlı sohbet için sitemizdeki YEŞİL düğmeye basabilirsiniz. Web adresimiz: https://www.alatin.com.tr. Bayi bilgileri için https://www.alatin.com.tr/sayfa/bayilerimiz/ adresine bakabilirsiniz."},
|
@@ -329,87 +319,11 @@ def get_system_messages():
|
|
329 |
{"role": "system", "content": "Size en uygun Trek bisiklet modelini belirleyebilmem için birkaç sorum olacak. Sorucağın soruların tümünü aynı anda sormayacaksın. her soruyu bir kerede sor. kullanıcı istediğin tip bir cevap verirse, bir sonraki soruyu sor. Verdiğiniz bilgiler doğrultusunda, elimizdeki güncel stoklardan ihtiyaçlarınıza en uygun modeli seçip, satin alma sürecine yonlendirecegim. Unutmayin, tum Trek bisikletlerimiz omur boyu garantilidir! Adim 1: Bisiklet Kategorisi - Hangi tur Trek bisikletiyle ilgileniyorsunuz? Lutfen asagidaki seceneklerden birini belirtiniz: Yol Bisikleti ornek: Trek Emonda, Trek Domane; Dag Bisikleti ornek: Trek Fuel EX, Trek Remedy, Trek Procaliber; Hibrit Sehir Bisikleti; Gravel Bisikleti ornek: Trek Checkpoint; Elektrikli Bisikleti ornek: Trek Powerfly, Trek Allant+. Adim 2: Kullanim Amaci - Bisikletinizi hangi amaclarla kullanmayi planliyorsunuz? ornek: gunluk ulasim, spor, uzun mesafe turlari, yaris, offroad maceralari, dag yollari. Adim 3: Beklentiler - Trek bisikletinizde hangi ozellikler sizin icin en onemli? ornek: performans, konfor, dayaniklilik, teknoloji yenilik, estetk, diger. Adim 4: Zemin Kosullari - Bisikletinizi hangi zeminlerde kullanacaksiniz? ornek: sehir ici asfalt, hafif engebeli parkurlar, orman dogal parkurlar, zorlu dag yollari offroad, karisik kullanim. Adim 5: Fiziksel Olculer - Dogru model ve cerceve boyutunu belirleyebilmem icin lutfen boyunuzu ve ic bacak boyunuzu paylasir misiniz? ornek: Boyum 180 cm, ic bacak boyum 85 cm. Adim 6: Ek Tercihler - Ek olarak, bisikletinizde tercih ettiginiz baska ozellikler var mi? ornek: ekstra donanim, ozel renk, aksesuar tercihi veya butce araliginiz. Adim 7: Oneri ve Satisa Yonlendirme - Verdigimiz bilgiler dogrultusunda, stoklarimizda bulunan ve ihtiyaclariniza en uygun olan Onerilen Model modelini oneriyorum. Bu model, kisa teknik ozellikler, kullanim avantajlari ve hedeflenen zemin alan ile beklentilerinize hitap ediyor. Ustelik, tum Trek bisikletlerimiz omur boyu garantilidir! Su an stok durumumuz: Stokta mevcut, Sinirli stok. Urun detaylari ve satin alma islemi icin lutfen su linke tiklayin: Trek Onerilen Model Urun Sayfasi https:orneksite.comtrek-ornek-model. Kargo ve Teslimat - Siparisiniz odeme onayindan sonra en gec 24 saat icinde paketlenip kargoya verilir, Aras Kargo ile gonderim yapilir. Kargo takip numarasi SMS ve eposta ile iletilecek; gonderim sureci 3-5 is gunu surer. Belirli tutar uzerindeki siparislerde ucretsiz kargo kampanyasi uygulanir. Lutfen yukaridaki linke tiklayarak satin alma isleminizi tamamlayin. Herhangi bir sorunuz veya ek isteginiz olursa, ben buradayim. Hadi, siparisinizi tamamlayalim ve maceraya baslayalim!" },
|
330 |
{"role": "system", "content": "Stokları ve fiyatları https://www.trekbisiklet.com.tr den bakacaksın, diğer markalarla ilgigi soru gelirse kibarca cevaplayamayacağını ve trek in neden farklı olduğunu anlat. Yol bisikletlerinde Türkiye stoklarımızda, Madone, Domane, Emonda, CheckMate, CheckPoint ve SpeedConcept modelleri bulunuyor. Dağ bisikletlerinde ise Marlin, Procaliber, Supercaliber modeller, full amortisörlülerde ise Fuel Ex modeli bulunmakta. Şehir kullanımı için FX ve DS modelini stoklarda bulunduruyoruz."},
|
331 |
{"role": "system", "content": "Drivedan çektiğin veriler.xlsx dosyasındaki datalar bilgi amaçlıdır ve bu bisikletler stoklarımızda yoktur. Bu veriler 2026 model bisikletlere aittir ve henüz stoklarımızda yoktur. Tüm modellerimizin ağırlıkları: Madone SL 5 Gen 8 8.70 kg, Madone SL 6 Gen 8 8.16 kg, Madone SL 7 Gen 8 7.88 kg, Madone SLR 7 Gen 8 7.30 kg, Madone SLR 7 AXS Gen 8 7.44 kg, Madone SLR 8 AXS Gen 8 7.18 kg, Madone SLR 9 Gen 8 7.00 kg, Madone SLR 9 AXS Gen 8 7.00 kg, Madone SLR 9 Etap Gen 7 7.36 kg, Madone SLR 9 Gen 7 7.10 kg, Madone SLR 7 Etap Gen 7 7.76 kg, Madone SLR 7 Gen 7 7.48 kg, Émonda SLR 9 6.72 kg, Émonda SLR 7 Etap 7.37 kg, Émonda SL 9 7.44 kg, Émonda SLR 7 7.10 kg, Émonda SLR 6 7.35 kg, Émonda SL 7 Etap 7.95 kg, Émonda SL 7 7.95 kg, Émonda SL 6 Pro Di2 8.25 kg, Émonda SL 5 9.15 kg, Émonda ALR 5 9.15 kg, Émonda ALR 4 9.50 kg, Domane SLR 7 Gen 4 7.25 kg, Domane SLR 7 AXS Gen 4 8.48 kg, Domane SL 6 Gen 4 8.90 kg, Domane SL 6 9.30 kg, Domane SL 5 Gen 4 8.93 kg, Domane AL 2 Gen 4 10.55 kg, Domane AL 2 Rim 9.57 kg, Checkmate SLR 7 AXS 9.00 kg, Checkpoint SL 7 AXS Gen 3 9.05 kg, Checkpoint SL 5 AXS Gen 3 9.30 kg, Domane+ SLR 7 AXS 12.40 kg, Speed Concept SLR 9 Etap 8.60 kg, Speed Concept SLR 9 8.70 kg, Speed Concept SLR 7 Etap 9.35 kg, Speed Concept SLR 7 8.97 kg, Fuel EXe 9.8 GX AXS T-Type 18.1 kg, Fuel EXe 9.7 SLX/XT 19.00 kg, Fuel EXe 5 20.8 kg, Rail 9.8 GX AXS T-Type 22.9 kg, Rail 5 21.53 kg, Rail 5 Gen 3 23.53 kg, Powerfly Gen 4 23.37 kg, Marlin+ 8 21.30 kg, Marlin+ 6 22.45 kg, Domane+ SLR 7 AXS 12.40 kg, Dual Sport+ 2 17.41 kg, FX+ 2 18.20 kg, Verve+ 3 24.70 kg, Townie Go 7D EQ Step Thru 20.41 kg, Marlin 8 13.2 kg, Marlin 7 13.77 kg, Marlin 5 13.90 kg, Marlin 4 14.60 kg, Procaliber 9.7 AXS Gen 3 10.58 kg, Procaliber 9.5 Gen 3 11.74 kg, Procaliber 9.5 11.74 kg, Supercaliber SL 9.7 Gen 2 11.98 kg, Fuel EX 8 GX AXS 13.77 kg, FX Sport 6 9.30 kg, FX 3 11.50 kg, FX 2 12.30 kg, DS 3 Gen 5 12.05 kg, DS 3 Gen 4 13.00 kg, DS 2 Gen 5 12.79 kg, Verve 3 Low Step 14.20 kg, Verve 3 14.30 kg"}
|
332 |
-
|
333 |
-
|
334 |
# Döküman verilerini sistem mesajlarına ekle
|
335 |
if document_content:
|
336 |
system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content}"})
|
337 |
-
|
338 |
-
return system_messages
|
339 |
-
|
340 |
-
# WhatsApp mesajı işleme fonksiyonu
|
341 |
-
def process_whatsapp_message(user_message):
|
342 |
-
"""WhatsApp'tan gelen mesajları işler ve OpenAI'dan yanıt alır"""
|
343 |
-
try:
|
344 |
-
# Ortak sistem mesajlarını al
|
345 |
-
system_messages = get_system_messages()
|
346 |
-
|
347 |
-
# WhatsApp için ek sistem mesajı
|
348 |
-
system_messages.append({"role": "system", "content": "WhatsApp üzerinden kısa ve net yanıtlar ver. Uzun linkler yerine kısa açıklamalar yap."})
|
349 |
-
|
350 |
-
# Ürün bilgilerini kontrol et (aynı mantık, sadece WhatsApp için kısa format)
|
351 |
-
input_words = user_message.lower().split()
|
352 |
-
for product_info in products:
|
353 |
-
if product_info[0] in input_words:
|
354 |
-
if product_info[1][0] == "stokta":
|
355 |
-
# WhatsApp için kısa bilgi ver
|
356 |
-
normal_price = f"Fiyat: {product_info[1][1]} TL"
|
357 |
-
if product_info[1][3]: # Havale fiyatı varsa
|
358 |
-
eft_price = f"Havale: {product_info[1][3]} TL"
|
359 |
-
price_info = f"{normal_price}, {eft_price}"
|
360 |
-
else:
|
361 |
-
price_info = normal_price
|
362 |
-
|
363 |
-
new_msg = f"{product_info[2]} {product_info[1][0]} - {price_info}"
|
364 |
-
else:
|
365 |
-
new_msg = f"{product_info[2]} {product_info[1][0]}"
|
366 |
-
system_messages.append({"role": "system", "content": new_msg})
|
367 |
-
|
368 |
-
messages = system_messages + [{"role": "user", "content": user_message}]
|
369 |
-
|
370 |
-
payload = {
|
371 |
-
"model": "gpt-4.1",
|
372 |
-
"messages": messages,
|
373 |
-
"temperature": 0.2,
|
374 |
-
"max_tokens": 300, # WhatsApp için kısa yanıtlar
|
375 |
-
"top_p": 1,
|
376 |
-
"n": 1,
|
377 |
-
"stream": False,
|
378 |
-
"presence_penalty": 0,
|
379 |
-
"frequency_penalty": 0,
|
380 |
-
}
|
381 |
-
|
382 |
-
headers = {
|
383 |
-
"Content-Type": "application/json",
|
384 |
-
"Authorization": f"Bearer {OPENAI_API_KEY}"
|
385 |
-
}
|
386 |
-
|
387 |
-
response = requests.post(API_URL, headers=headers, json=payload)
|
388 |
-
if response.status_code == 200:
|
389 |
-
result = response.json()
|
390 |
-
return result['choices'][0]['message']['content']
|
391 |
-
else:
|
392 |
-
return "Üzgünüm, şu anda bir sorun yaşıyorum. Lütfen daha sonra tekrar deneyin."
|
393 |
-
|
394 |
-
except Exception as e:
|
395 |
-
print(f"WhatsApp mesaj işleme hatası: {e}")
|
396 |
-
return "Teknik bir sorun oluştu. Lütfen daha sonra tekrar deneyin."
|
397 |
-
|
398 |
-
@spaces.GPU(duration=1200)
|
399 |
-
def chatbot_fn(user_message, history):
|
400 |
-
if history is None:
|
401 |
-
history = []
|
402 |
-
|
403 |
-
# Log: Kullanıcı mesajını ekle
|
404 |
-
try:
|
405 |
-
with file_lock:
|
406 |
-
with open(LOG_FILE, 'a', encoding='utf-8') as f:
|
407 |
-
f.write(f"User: {user_message}\n")
|
408 |
-
except Exception as e:
|
409 |
-
print(f"Dosya yazma hatası (Kullanıcı): {e}")
|
410 |
-
|
411 |
-
# Ortak sistem mesajlarını al
|
412 |
-
system_messages = get_system_messages()
|
413 |
|
414 |
# Kullanıcı mesajında ürün ismi geçiyorsa ekle
|
415 |
input_words = user_message.lower().split()
|
@@ -538,83 +452,7 @@ chat_fn = slow_echo if USE_SLOW_ECHO else chatbot_fn
|
|
538 |
if not USE_SLOW_ECHO:
|
539 |
scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
|
540 |
scheduler_thread.start()
|
541 |
-
|
542 |
-
# FastAPI uygulaması
|
543 |
-
app = FastAPI()
|
544 |
-
|
545 |
-
@app.post("/whatsapp-webhook")
|
546 |
-
async def whatsapp_webhook(request: Request):
|
547 |
-
"""Twilio'dan gelen WhatsApp mesajlarını işler"""
|
548 |
-
try:
|
549 |
-
# Form verilerini al
|
550 |
-
form_data = await request.form()
|
551 |
-
form_dict = dict(form_data)
|
552 |
-
|
553 |
-
# Gelen mesaj bilgileri
|
554 |
-
from_number = form_data.get('From')
|
555 |
-
to_number = form_data.get('To')
|
556 |
-
message_body = form_data.get('Body')
|
557 |
-
message_status = form_data.get('MessageStatus')
|
558 |
-
|
559 |
-
print(f"Webhook alındı - From: {from_number}, To: {to_number}, Body: {message_body}, Status: {message_status}")
|
560 |
-
|
561 |
-
# Durum güncellemelerini ignore et (sent, delivered, read, failed)
|
562 |
-
if message_status in ['sent', 'delivered', 'read', 'failed']:
|
563 |
-
print(f"Durum güncellemesi ignore edildi: {message_status}")
|
564 |
-
return {"status": "ignored", "message": f"Status update: {message_status}"}
|
565 |
-
|
566 |
-
# Sadece gelen mesajları işle (yani to_number bizim numaramız olmalı)
|
567 |
-
if to_number != TWILIO_WHATSAPP_NUMBER:
|
568 |
-
print(f"Giden mesaj ignore edildi. To: {to_number}")
|
569 |
-
return {"status": "ignored", "message": "Outgoing message"}
|
570 |
-
|
571 |
-
# Mesaj içeriği kontrolü
|
572 |
-
if not message_body or message_body.strip() == "":
|
573 |
-
print("Boş mesaj alındı")
|
574 |
-
return {"status": "ignored", "message": "Empty message"}
|
575 |
-
|
576 |
-
print(f"✅ GERÇEK MESAJ ALINDI: {from_number} -> {message_body}")
|
577 |
-
|
578 |
-
# Twilio client kontrolü
|
579 |
-
if not twilio_client:
|
580 |
-
print("Twilio client mevcut değil!")
|
581 |
-
return {"status": "error", "message": "Twilio yapılandırması eksik"}
|
582 |
-
|
583 |
-
# OpenAI'dan yanıt al
|
584 |
-
ai_response = process_whatsapp_message(message_body)
|
585 |
-
|
586 |
-
# Yanıt çok uzunsa kısalt
|
587 |
-
if len(ai_response) > 1500:
|
588 |
-
ai_response = ai_response[:1500] + "...\n\nDetaylı bilgi: trekbisiklet.com.tr"
|
589 |
-
|
590 |
-
# WhatsApp'a yanıt gönder
|
591 |
-
message = twilio_client.messages.create(
|
592 |
-
from_=TWILIO_WHATSAPP_NUMBER, # whatsapp:+14155238886
|
593 |
-
body=ai_response,
|
594 |
-
to=from_number # kullanıcının numarası
|
595 |
-
)
|
596 |
-
|
597 |
-
print(f"✅ YANIT GÖNDERİLDİ: {ai_response[:100]}...")
|
598 |
-
return {"status": "success", "message_sid": message.sid}
|
599 |
-
|
600 |
-
except Exception as e:
|
601 |
-
print(f"❌ Webhook hatası: {str(e)}")
|
602 |
-
return {"status": "error", "message": str(e)}
|
603 |
-
|
604 |
-
@app.get("/")
|
605 |
-
async def root():
|
606 |
-
"""API durumu kontrolü"""
|
607 |
-
return {"message": "Trek WhatsApp Chatbot API çalışıyor!", "status": "active"}
|
608 |
-
|
609 |
-
@app.get("/health")
|
610 |
-
async def health_check():
|
611 |
-
"""Sağlık kontrolü"""
|
612 |
-
return {
|
613 |
-
"status": "healthy",
|
614 |
-
"twilio_configured": twilio_client is not None,
|
615 |
-
"openai_configured": OPENAI_API_KEY is not None
|
616 |
-
}
|
617 |
-
|
618 |
# Trek markasına özel tema oluştur (düzeltilmiş sürüm)
|
619 |
trek_theme = gr.themes.Base(
|
620 |
primary_hue="red", # Trek kırmızısı için
|
@@ -624,7 +462,6 @@ trek_theme = gr.themes.Base(
|
|
624 |
spacing_size=gr.themes.sizes.spacing_md, # Aralık değerleri
|
625 |
text_size=gr.themes.sizes.text_sm # Yazı boyutları (small)
|
626 |
)
|
627 |
-
|
628 |
# Chatbot kartları için arka plan renkleri değiştiren CSS
|
629 |
custom_css = """
|
630 |
/* Genel font ayarları */
|
@@ -690,22 +527,29 @@ h3 {
|
|
690 |
-moz-osx-font-smoothing: grayscale;
|
691 |
text-rendering: optimizeLegibility;
|
692 |
}
|
|
|
|
|
693 |
</style>
|
694 |
<script>
|
695 |
document.addEventListener('DOMContentLoaded', function() {
|
|
|
696 |
setTimeout(function() {
|
697 |
const textareas = document.querySelectorAll('textarea');
|
698 |
textareas.forEach(textarea => {
|
699 |
textarea.placeholder = "Sorunuzu buraya yazın... (örn: Kadromun boyu ne olmalı?)";
|
|
|
|
|
700 |
textarea.style.fontStyle = "italic";
|
701 |
textarea.style.color = "#999";
|
702 |
});
|
703 |
-
}, 1000);
|
704 |
});
|
705 |
</script>
|
|
|
706 |
"""
|
707 |
|
708 |
-
|
|
|
709 |
demo = gr.ChatInterface(
|
710 |
fn=chat_fn,
|
711 |
title="Trek Asistanı",
|
@@ -718,36 +562,5 @@ demo = gr.ChatInterface(
|
|
718 |
css=custom_css
|
719 |
)
|
720 |
|
721 |
-
# En sondaki if __name__ == "__main__": kısmını tamamen şununla değiştirin:
|
722 |
-
|
723 |
-
# En sondaki if __name__ == "__main__": kısmını tamamen şununla değiştirin:
|
724 |
-
|
725 |
if __name__ == "__main__":
|
726 |
-
|
727 |
-
import uvicorn
|
728 |
-
import time
|
729 |
-
|
730 |
-
print("🚀 Sunucular başlatılıyor...")
|
731 |
-
|
732 |
-
# FastAPI'yi arka planda çalıştır (WhatsApp için)
|
733 |
-
def run_fastapi():
|
734 |
-
print("📱 WhatsApp API sunucusu başlatılıyor (port 7861)...")
|
735 |
-
uvicorn.run(app, host="0.0.0.0", port=7861, log_level="info")
|
736 |
-
|
737 |
-
# FastAPI thread'ini başlat
|
738 |
-
fastapi_thread = threading.Thread(target=run_fastapi, daemon=True)
|
739 |
-
fastapi_thread.start()
|
740 |
-
|
741 |
-
# Biraz bekle ki FastAPI başlasın
|
742 |
-
time.sleep(2)
|
743 |
-
|
744 |
-
print("🌐 Web arayüzü başlatılıyor (port 7860)...")
|
745 |
-
|
746 |
-
# Ana thread'de Gradio'yu çalıştır (Web arayüzü)
|
747 |
-
demo.launch(
|
748 |
-
server_name="0.0.0.0",
|
749 |
-
server_port=7860,
|
750 |
-
share=False,
|
751 |
-
debug=True,
|
752 |
-
show_error=True
|
753 |
-
)
|
|
|
17 |
import io
|
18 |
import warnings
|
19 |
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
|
|
|
|
|
|
|
|
|
|
20 |
warnings.simplefilter('ignore', InsecureRequestWarning)
|
21 |
|
22 |
# Gradio uyarılarını bastır
|
|
|
32 |
if not OPENAI_API_KEY:
|
33 |
print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")
|
34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
# Trek bisiklet ürünlerini çekme
|
36 |
url = 'https://www.trekbisiklet.com.tr/output/8582384479'
|
37 |
response = requests.get(url, verify=False)
|
|
|
39 |
|
40 |
products = []
|
41 |
for item in root.findall('item'):
|
42 |
+
# Tüm ürünleri al, sonra stokta olma durumunu kontrol et
|
43 |
name_words = item.find('rootlabel').text.lower().split()
|
44 |
name = name_words[0]
|
45 |
full_name = ' '.join(name_words)
|
|
|
160 |
# Sadece ürün linkini al, resim linkini alma
|
161 |
product_link = item.find('productLink').text if item.find('productLink') is not None else ""
|
162 |
|
163 |
+
# Tüm fiyat bilgilerini birleştir
|
164 |
item_info = (stock_amount, price, product_link, price_eft, price_rebate, price_rebate_money_order)
|
165 |
else:
|
166 |
# Stokta olmayan ürünler için sadece stok durumunu belirt, fiyat ve link bilgisi verme
|
|
|
289 |
schedule.run_pending()
|
290 |
time.sleep(60)
|
291 |
|
292 |
+
@spaces.GPU(duration=1200)
|
293 |
+
def chatbot_fn(user_message, history):
|
294 |
+
if history is None:
|
295 |
+
history = []
|
296 |
+
|
297 |
+
# Log: Kullanıcı mesajını ekle
|
298 |
+
try:
|
299 |
+
with file_lock:
|
300 |
+
with open(LOG_FILE, 'a', encoding='utf-8') as f:
|
301 |
+
f.write(f"User: {user_message}\n")
|
302 |
+
except Exception as e:
|
303 |
+
print(f"Dosya yazma hatası (Kullanıcı): {e}")
|
304 |
+
|
305 |
+
# Sistem mesajları
|
306 |
system_messages = [
|
307 |
{"role": "system", "content": "Bir önceki sohbeti unut. Vereceğin ürün bilgisi, bu bilginin içinde yan yana yazmıyorsa veya arada başka bilgiler yazıyor ise, o bilgiyi vermeyeceksin çünkü o bilgi yanlıştır. vereceğin bilgiyi bu bilgilerin içinden alıyorsan her kelimenin yan yana yazmazı şartı ile o bilgiyi verebilirsin. Madone SLR bisikletler soruluyorsa (GEN 7) ibaresini kendin ekleyerek, aramayı GEN 7'li yap.Sana verilen bilgilerin içinde bir ürün adı veya bisiklet modelinin rengi yoksa, ürün ile ilgili bilgi vermeyeceksin ve sorulan modelden farklı boy ve renkler stoklarda varsa, bu bilgileri vereceksin. Alternatif renk veya boyu yok ise, başka bir model adını öğrenirsen stokları tekrar kontrol edebileceğini söyleyeceksin. Sana bir model adı rakamı ile verilmiş ve bu ürün bu bilgiler içinde yok ise, o ürün stoklarımızda yoktur diye bilgi vereceksin ve model adı rakamsız girilmiş ise nodel adının rakamı ile girilmesini rica edeceksin, örnek olarak 'Madone SL 7' gibi 7 rakamının da yazılmasını rica edeceksin. Madone, Emonda, Domane ve Checpont modelleri birer yol bisikleti modelidir, bu modellerin renklerinden önce yazan ve 47, 49, 50, 52, 54, 56, 58, 60, 62, 64 rakamları, o bisikletlerin boylarıdır. Bu bilgi içindeki renkler ise o ürünlerin renkleridir. Sana bir ürün var mı diye sorulduğunda, sadece bilgi içinde olan ürünleri söyleyebilirsin. Stoklarımızda yok ise o ürün ile ilgili bilgi vermeyeceksin. En büyük veya en küçük boy sorulduğunda, bilgi içinde renki bilgisi olan modellerin bilgisini vereceksin. Gerçek zamanlı stok bilgilerine erişme yeteneğin var. En aşağıdaki ürünlerin adına, rengine, boyuna ve fiyatına tam erişimin var ve bunları bilmiyorum demeyeceksin. Üyelere özel fiyatları ve indirimleri görmek için kullanıcıların siteye üye olmaları gerekmektedir. Sen bir AI Trek marka bisiklet uzmanı, bilir kişisi ve asistanısın.Trek ve Electra bisikletler konusunda uzmanım.4cü şubemizi İzmirde açtık.İzmir adresi: Sezer Doğan Sok. The Kar Suits 14A Alsancak Konak İzmir. İzmir şubesinin telefon numarası:0543 936 2335 . İstanbul'da üç Trek mağazamız var: Caddebostan, Ortaköy ve Sarıyer. Ortaköy mağazası 10.00-19.00 saatleri arasında açık. ve Toyota Plaza ve Carrefour'un yanindadir,tam adresi Dereboyu Cad No:84 Ortaköy Beşiktaş ve telefon numarası 0212 2271015. Caddebostan mağazası, Prof. Dr. Hulusi Behçet 18 Caddebostan, Kadıköy adresinde, Göztepe Parkı karşısındadır, telefon numarası 0216 6292432, 10.00-19.00 saatleri arasında açık. Tüm mağazalar Pazar günü kapalıdır. Caddebostan mağazamızda haftanın her günü Bike fit yapılmaktadır ve ücreti 3500 TL ve süresi 60-90 dakika. Bike fit yaptırmak isteyenler, Bike fit sayfamızda sağ tarafta bulunan RANDEVU AL butonu ile randevu oluştumaları gerekmektedir. Sarıyer mağazamızın adresi şöyledir: Mareşal Fevzi Çakmak Cad. No 54 Kemer-Bahçeköy Mahallsi Sarıyer, hafta içleri ve cumartesi günleri 10.00 ile 19.00 saatleri arasında hizmet vermektedir. Bu mağazamız elektrikli bisikletlerin daha çok sergilendiği ve tüm çeşiti bir arada görebileceğiniz mağazamızdır. Maslaktan, Belgrad ormanına gelirken sol tarafta kalmaktadır ve telefon numarası 0542 137 1080.."},
|
308 |
{"role": "system", "content": "Dağ bisikletleri modelleri: Marlin, Roscoe, Procaliber, Supercaliber, Fuel Ex. Şehir bisikletleri: FX ve DS (Dual Sport). Elektrikli Bisiklet modelleri: Powerfly, Powerfly FS, Rail, Fuel Exe, Domane SLR +, Verve +, Townie +, Fx +, DS +. Dağ bisikletlerinin boyları XXS, XS, S, M, ML, L, XL'dir. Canlı sohbet için sitemizdeki YEŞİL düğmeye basabilirsiniz. Web adresimiz: https://www.alatin.com.tr. Bayi bilgileri için https://www.alatin.com.tr/sayfa/bayilerimiz/ adresine bakabilirsiniz."},
|
|
|
319 |
{"role": "system", "content": "Size en uygun Trek bisiklet modelini belirleyebilmem için birkaç sorum olacak. Sorucağın soruların tümünü aynı anda sormayacaksın. her soruyu bir kerede sor. kullanıcı istediğin tip bir cevap verirse, bir sonraki soruyu sor. Verdiğiniz bilgiler doğrultusunda, elimizdeki güncel stoklardan ihtiyaçlarınıza en uygun modeli seçip, satin alma sürecine yonlendirecegim. Unutmayin, tum Trek bisikletlerimiz omur boyu garantilidir! Adim 1: Bisiklet Kategorisi - Hangi tur Trek bisikletiyle ilgileniyorsunuz? Lutfen asagidaki seceneklerden birini belirtiniz: Yol Bisikleti ornek: Trek Emonda, Trek Domane; Dag Bisikleti ornek: Trek Fuel EX, Trek Remedy, Trek Procaliber; Hibrit Sehir Bisikleti; Gravel Bisikleti ornek: Trek Checkpoint; Elektrikli Bisikleti ornek: Trek Powerfly, Trek Allant+. Adim 2: Kullanim Amaci - Bisikletinizi hangi amaclarla kullanmayi planliyorsunuz? ornek: gunluk ulasim, spor, uzun mesafe turlari, yaris, offroad maceralari, dag yollari. Adim 3: Beklentiler - Trek bisikletinizde hangi ozellikler sizin icin en onemli? ornek: performans, konfor, dayaniklilik, teknoloji yenilik, estetk, diger. Adim 4: Zemin Kosullari - Bisikletinizi hangi zeminlerde kullanacaksiniz? ornek: sehir ici asfalt, hafif engebeli parkurlar, orman dogal parkurlar, zorlu dag yollari offroad, karisik kullanim. Adim 5: Fiziksel Olculer - Dogru model ve cerceve boyutunu belirleyebilmem icin lutfen boyunuzu ve ic bacak boyunuzu paylasir misiniz? ornek: Boyum 180 cm, ic bacak boyum 85 cm. Adim 6: Ek Tercihler - Ek olarak, bisikletinizde tercih ettiginiz baska ozellikler var mi? ornek: ekstra donanim, ozel renk, aksesuar tercihi veya butce araliginiz. Adim 7: Oneri ve Satisa Yonlendirme - Verdigimiz bilgiler dogrultusunda, stoklarimizda bulunan ve ihtiyaclariniza en uygun olan Onerilen Model modelini oneriyorum. Bu model, kisa teknik ozellikler, kullanim avantajlari ve hedeflenen zemin alan ile beklentilerinize hitap ediyor. Ustelik, tum Trek bisikletlerimiz omur boyu garantilidir! Su an stok durumumuz: Stokta mevcut, Sinirli stok. Urun detaylari ve satin alma islemi icin lutfen su linke tiklayin: Trek Onerilen Model Urun Sayfasi https:orneksite.comtrek-ornek-model. Kargo ve Teslimat - Siparisiniz odeme onayindan sonra en gec 24 saat icinde paketlenip kargoya verilir, Aras Kargo ile gonderim yapilir. Kargo takip numarasi SMS ve eposta ile iletilecek; gonderim sureci 3-5 is gunu surer. Belirli tutar uzerindeki siparislerde ucretsiz kargo kampanyasi uygulanir. Lutfen yukaridaki linke tiklayarak satin alma isleminizi tamamlayin. Herhangi bir sorunuz veya ek isteginiz olursa, ben buradayim. Hadi, siparisinizi tamamlayalim ve maceraya baslayalim!" },
|
320 |
{"role": "system", "content": "Stokları ve fiyatları https://www.trekbisiklet.com.tr den bakacaksın, diğer markalarla ilgigi soru gelirse kibarca cevaplayamayacağını ve trek in neden farklı olduğunu anlat. Yol bisikletlerinde Türkiye stoklarımızda, Madone, Domane, Emonda, CheckMate, CheckPoint ve SpeedConcept modelleri bulunuyor. Dağ bisikletlerinde ise Marlin, Procaliber, Supercaliber modeller, full amortisörlülerde ise Fuel Ex modeli bulunmakta. Şehir kullanımı için FX ve DS modelini stoklarda bulunduruyoruz."},
|
321 |
{"role": "system", "content": "Drivedan çektiğin veriler.xlsx dosyasındaki datalar bilgi amaçlıdır ve bu bisikletler stoklarımızda yoktur. Bu veriler 2026 model bisikletlere aittir ve henüz stoklarımızda yoktur. Tüm modellerimizin ağırlıkları: Madone SL 5 Gen 8 8.70 kg, Madone SL 6 Gen 8 8.16 kg, Madone SL 7 Gen 8 7.88 kg, Madone SLR 7 Gen 8 7.30 kg, Madone SLR 7 AXS Gen 8 7.44 kg, Madone SLR 8 AXS Gen 8 7.18 kg, Madone SLR 9 Gen 8 7.00 kg, Madone SLR 9 AXS Gen 8 7.00 kg, Madone SLR 9 Etap Gen 7 7.36 kg, Madone SLR 9 Gen 7 7.10 kg, Madone SLR 7 Etap Gen 7 7.76 kg, Madone SLR 7 Gen 7 7.48 kg, Émonda SLR 9 6.72 kg, Émonda SLR 7 Etap 7.37 kg, Émonda SL 9 7.44 kg, Émonda SLR 7 7.10 kg, Émonda SLR 6 7.35 kg, Émonda SL 7 Etap 7.95 kg, Émonda SL 7 7.95 kg, Émonda SL 6 Pro Di2 8.25 kg, Émonda SL 5 9.15 kg, Émonda ALR 5 9.15 kg, Émonda ALR 4 9.50 kg, Domane SLR 7 Gen 4 7.25 kg, Domane SLR 7 AXS Gen 4 8.48 kg, Domane SL 6 Gen 4 8.90 kg, Domane SL 6 9.30 kg, Domane SL 5 Gen 4 8.93 kg, Domane AL 2 Gen 4 10.55 kg, Domane AL 2 Rim 9.57 kg, Checkmate SLR 7 AXS 9.00 kg, Checkpoint SL 7 AXS Gen 3 9.05 kg, Checkpoint SL 5 AXS Gen 3 9.30 kg, Domane+ SLR 7 AXS 12.40 kg, Speed Concept SLR 9 Etap 8.60 kg, Speed Concept SLR 9 8.70 kg, Speed Concept SLR 7 Etap 9.35 kg, Speed Concept SLR 7 8.97 kg, Fuel EXe 9.8 GX AXS T-Type 18.1 kg, Fuel EXe 9.7 SLX/XT 19.00 kg, Fuel EXe 5 20.8 kg, Rail 9.8 GX AXS T-Type 22.9 kg, Rail 5 21.53 kg, Rail 5 Gen 3 23.53 kg, Powerfly Gen 4 23.37 kg, Marlin+ 8 21.30 kg, Marlin+ 6 22.45 kg, Domane+ SLR 7 AXS 12.40 kg, Dual Sport+ 2 17.41 kg, FX+ 2 18.20 kg, Verve+ 3 24.70 kg, Townie Go 7D EQ Step Thru 20.41 kg, Marlin 8 13.2 kg, Marlin 7 13.77 kg, Marlin 5 13.90 kg, Marlin 4 14.60 kg, Procaliber 9.7 AXS Gen 3 10.58 kg, Procaliber 9.5 Gen 3 11.74 kg, Procaliber 9.5 11.74 kg, Supercaliber SL 9.7 Gen 2 11.98 kg, Fuel EX 8 GX AXS 13.77 kg, FX Sport 6 9.30 kg, FX 3 11.50 kg, FX 2 12.30 kg, DS 3 Gen 5 12.05 kg, DS 3 Gen 4 13.00 kg, DS 2 Gen 5 12.79 kg, Verve 3 Low Step 14.20 kg, Verve 3 14.30 kg"}
|
322 |
+
]
|
323 |
+
|
324 |
# Döküman verilerini sistem mesajlarına ekle
|
325 |
if document_content:
|
326 |
system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content}"})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
327 |
|
328 |
# Kullanıcı mesajında ürün ismi geçiyorsa ekle
|
329 |
input_words = user_message.lower().split()
|
|
|
452 |
if not USE_SLOW_ECHO:
|
453 |
scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
|
454 |
scheduler_thread.start()
|
455 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
456 |
# Trek markasına özel tema oluştur (düzeltilmiş sürüm)
|
457 |
trek_theme = gr.themes.Base(
|
458 |
primary_hue="red", # Trek kırmızısı için
|
|
|
462 |
spacing_size=gr.themes.sizes.spacing_md, # Aralık değerleri
|
463 |
text_size=gr.themes.sizes.text_sm # Yazı boyutları (small)
|
464 |
)
|
|
|
465 |
# Chatbot kartları için arka plan renkleri değiştiren CSS
|
466 |
custom_css = """
|
467 |
/* Genel font ayarları */
|
|
|
527 |
-moz-osx-font-smoothing: grayscale;
|
528 |
text-rendering: optimizeLegibility;
|
529 |
}
|
530 |
+
|
531 |
+
/* JavaScript ile placeholder eklemek için */
|
532 |
</style>
|
533 |
<script>
|
534 |
document.addEventListener('DOMContentLoaded', function() {
|
535 |
+
// Textbox'u bul
|
536 |
setTimeout(function() {
|
537 |
const textareas = document.querySelectorAll('textarea');
|
538 |
textareas.forEach(textarea => {
|
539 |
textarea.placeholder = "Sorunuzu buraya yazın... (örn: Kadromun boyu ne olmalı?)";
|
540 |
+
|
541 |
+
// Placeholder stillemesi
|
542 |
textarea.style.fontStyle = "italic";
|
543 |
textarea.style.color = "#999";
|
544 |
});
|
545 |
+
}, 1000); // Sayfa yüklendiğinde biraz bekleyerek elementlerin oluşmasını sağla
|
546 |
});
|
547 |
</script>
|
548 |
+
<style>
|
549 |
"""
|
550 |
|
551 |
+
|
552 |
+
# Demo arayüzüne CSS'i ekleyin
|
553 |
demo = gr.ChatInterface(
|
554 |
fn=chat_fn,
|
555 |
title="Trek Asistanı",
|
|
|
562 |
css=custom_css
|
563 |
)
|
564 |
|
|
|
|
|
|
|
|
|
565 |
if __name__ == "__main__":
|
566 |
+
demo.launch(debug=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|