SamiKoen commited on
Commit
9c90b18
·
verified ·
1 Parent(s): 932a3a8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -216
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
- # Tüm ürünleri al, sonra stokta olma durumunu kontrol et
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
- # Tüm fiyat bilgilerini birleştir
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
- # Ortak sistem mesajları fonksiyonu
314
- def get_system_messages():
315
- """Hem web hem WhatsApp için ortak sistem mesajları"""
 
 
 
 
 
 
 
 
 
 
 
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
- # Demo arayüzü
 
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
- import threading
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)