File size: 38,256 Bytes
c051efa
2142c80
 
f3ff1c7
e4aef12
e7590dc
 
 
e4aef12
292b39a
e4aef12
 
7982c39
e4aef12
 
 
 
e8fdc32
 
 
292b39a
 
 
fce7101
5e7c2bd
61d867f
75e9167
7acd937
8b7994d
2142c80
 
97be484
75e9167
f3ff1c7
e4aef12
 
13de5a1
e4aef12
 
 
 
483e37d
517e045
 
 
 
 
 
 
 
 
 
 
483e37d
517e045
 
483e37d
517e045
 
483e37d
517e045
 
 
 
 
a9d9fb6
 
 
 
 
 
 
 
 
 
517e045
a9d9fb6
517e045
 
 
483e37d
 
a9d9fb6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8f66c72
a9d9fb6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517e045
 
 
 
98651db
517e045
 
 
 
 
 
e4aef12
e7590dc
cb8389b
 
75e9167
5942ddb
42c9a95
7a7b7b1
292b39a
 
 
 
e7590dc
ce90fa8
e4aef12
 
 
 
 
 
 
7982c39
e4aef12
 
 
 
 
ce90fa8
7982c39
e4aef12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce90fa8
e4aef12
 
 
 
ce90fa8
 
e4aef12
 
 
 
 
 
 
 
ce90fa8
 
e4aef12
 
 
 
 
 
7982c39
292b39a
e7590dc
292b39a
 
 
 
e4aef12
292b39a
 
e4aef12
292b39a
 
 
 
e4aef12
292b39a
 
 
e4aef12
 
292b39a
 
 
e4aef12
292b39a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e7590dc
 
8f42238
f3e1c89
 
 
 
 
61d867f
e7590dc
 
 
 
e4aef12
292b39a
e182566
5e7c2bd
e182566
292b39a
 
 
 
 
 
 
 
 
be44243
cce74cf
72450d2
620c8a7
 
 
 
 
 
 
 
 
 
 
 
 
3306257
7982c39
 
 
e4aef12
 
 
 
 
 
517e045
 
 
 
a0fb79d
 
 
06ef9f5
 
 
 
 
 
517e045
06ef9f5
517e045
 
a0fb79d
517e045
 
 
 
 
 
 
a0fb79d
a9d9fb6
 
 
517e045
a9d9fb6
 
517e045
a9d9fb6
517e045
a0fb79d
517e045
 
 
 
679154a
 
06ef9f5
679154a
06ef9f5
e694929
06ef9f5
517e045
 
 
 
 
 
 
 
e4aef12
ce90fa8
292b39a
 
3306257
e8fdc32
3306257
e4aef12
 
3306257
9cb89a1
3306257
 
 
292b39a
 
 
 
5e7c2bd
3306257
 
ce90fa8
292b39a
2203b4d
6328941
292b39a
3306257
 
 
 
 
 
 
 
292b39a
6328941
ce90fa8
3306257
75e9167
3306257
e15e9df
3306257
292b39a
 
 
 
0a6cb1a
292b39a
 
75e9167
292b39a
 
 
0a6cb1a
0b9cf76
292b39a
 
 
 
ce90fa8
292b39a
 
 
 
 
 
 
e7590dc
e8fdc32
bb1d510
e935a11
 
 
 
 
 
bb1d510
e935a11
cac1e6c
c902f07
9bbad34
 
 
 
 
fae5647
e7e1ccd
9bbad34
 
e7e1ccd
fae5647
9bbad34
 
 
 
cac1e6c
fae5647
9bbad34
 
 
 
 
 
 
 
 
 
 
 
cac1e6c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9bbad34
 
 
 
 
e7e1ccd
841a6fd
54d2e45
 
 
 
 
 
 
 
f5deff0
54d2e45
 
 
 
 
 
 
 
 
 
 
c902f07
9bbad34
c902f07
 
 
fae5647
c902f07
 
 
 
 
9bbad34
292b39a
4300c5d
292b39a
7982c39
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
import gradio as gr
import os
import json
import requests
import xml.etree.ElementTree as ET
import schedule
import time
import threading
from huggingface_hub import HfApi, create_repo, hf_hub_download
import warnings
import pandas as pd
from docx import Document
import spaces
from google.oauth2.service_account import Credentials
from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseDownload
import io
import warnings
from requests.packages.urllib3.exceptions import InsecureRequestWarning
warnings.simplefilter('ignore', InsecureRequestWarning)

# Gradio uyarılarını bastır
warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")

# Log dosyası adı ve yolu
LOG_FILE = '/data/chat_logs.txt' if os.path.exists('/data') else 'chat_logs.txt'
print(f"Dosya yolu: {os.path.abspath(LOG_FILE)}")

# API ayarları
API_URL = "https://api.openai.com/v1/chat/completions"
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
if not OPENAI_API_KEY:
    print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")

# Trek bisiklet ürünlerini çekme
url = 'https://www.trekbisiklet.com.tr/output/8582384479'
response = requests.get(url, verify=False)
root = ET.fromstring(response.content)

products = []
for item in root.findall('item'):
        # Tüm ürünleri al, sonra stokta olma durumunu kontrol et
    name_words = item.find('rootlabel').text.lower().split()
    name = name_words[0]
    full_name = ' '.join(name_words)
    
    stock_amount = "stokta" if item.find('stockAmount').text > '0' else "stokta değil"
    
    # Stokta olmayan ürünler için fiyat/link bilgisi eklemiyoruz
    if stock_amount == "stokta":
        # Normal fiyat bilgisini al
        price_str = item.find('priceTaxWithCur').text if item.find('priceTaxWithCur') is not None else "Fiyat bilgisi yok"
        
        # EFT fiyatını al (havale indirimli orijinal fiyat)
        price_eft_str = item.find('priceEft').text if item.find('priceEft') is not None else ""
        
        # İndirimli fiyatı al (kampanyalı fiyat)
        price_rebate_str = item.find('priceRebateWithTax').text if item.find('priceRebateWithTax') is not None else ""
        
        # Havale indirimi fiyatını al (havale indirimli kampanyalı fiyat)
        price_rebate_money_order_str = item.find('priceRebateWithMoneyOrderWithTax').text if item.find('priceRebateWithMoneyOrderWithTax') is not None else ""
        
        # Normal fiyatı yuvarla
        try:
            price_float = float(price_str)
            # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
            if price_float > 200000:
                price = str(round(price_float / 5000) * 5000)  # En yakın 5000'e yuvarla
            # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
            elif price_float > 30000:
                price = str(round(price_float / 1000) * 1000)  # En yakın 1000'e yuvarla
            # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
            elif price_float > 10000:
                price = str(round(price_float / 100) * 100)  # En yakın 100'e yuvarla
            # Diğer durumlarda en yakın 10'luk basamağa yuvarla
            else:
                price = str(round(price_float / 10) * 10)  # En yakın 10'a yuvarla
        except (ValueError, TypeError):
            price = price_str  # Sayıya dönüştürülemezse olduğu gibi bırak
        
        # Havale indirimli orijinal fiyatı yuvarla (varsa)
        if price_eft_str:
            try:
                price_eft_float = float(price_eft_str)
                # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
                if price_eft_float > 200000:
                    price_eft = str(round(price_eft_float / 5000) * 5000)
                # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla 
                elif price_eft_float > 30000:
                    price_eft = str(round(price_eft_float / 1000) * 1000)
                # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
                elif price_eft_float > 10000:
                    price_eft = str(round(price_eft_float / 100) * 100)
                # Diğer durumlarda en yakın 10'luk basamağa yuvarla
                else:
                    price_eft = str(round(price_eft_float / 10) * 10)
            except (ValueError, TypeError):
                price_eft = price_eft_str
        else:
            # Havale indirimli fiyat verilmemişse, orijinal fiyattan %2.5 indirim hesapla
            try:
                price_eft_float = price_float * 0.975  # %2.5 indirim
                # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
                if price_eft_float > 200000:
                    price_eft = str(round(price_eft_float / 5000) * 5000)
                # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla 
                elif price_eft_float > 30000:
                    price_eft = str(round(price_eft_float / 1000) * 1000)
                # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
                elif price_eft_float > 10000:
                    price_eft = str(round(price_eft_float / 100) * 100)
                # Diğer durumlarda en yakın 10'luk basamağa yuvarla
                else:
                    price_eft = str(round(price_eft_float / 10) * 10)
            except (ValueError, TypeError):
                price_eft = ""
        
        # İndirimli fiyatı yuvarla
        try:
            if price_rebate_str:
                price_rebate_float = float(price_rebate_str)
                # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
                if price_rebate_float > 200000:
                    price_rebate = str(round(price_rebate_float / 5000) * 5000)
                # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
                elif price_rebate_float > 30000:
                    price_rebate = str(round(price_rebate_float / 1000) * 1000)
                # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
                elif price_rebate_float > 10000:
                    price_rebate = str(round(price_rebate_float / 100) * 100)
                # Diğer durumlarda en yakın 10'luk basamağa yuvarla
                else:
                    price_rebate = str(round(price_rebate_float / 10) * 10)
            else:
                price_rebate = ""
        except (ValueError, TypeError):
            price_rebate = price_rebate_str
        
        # Havale indirimi kampanyalı fiyatı yuvarla
        try:
            if price_rebate_money_order_str:
                price_rebate_money_order_float = float(price_rebate_money_order_str)
                # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
                if price_rebate_money_order_float > 200000:
                    price_rebate_money_order = str(round(price_rebate_money_order_float / 5000) * 5000)
                # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
                elif price_rebate_money_order_float > 30000:
                    price_rebate_money_order = str(round(price_rebate_money_order_float / 1000) * 1000)
                # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
                elif price_rebate_money_order_float > 10000:
                    price_rebate_money_order = str(round(price_rebate_money_order_float / 100) * 100)
                # Diğer durumlarda en yakın 10'luk basamağa yuvarla
                else:
                    price_rebate_money_order = str(round(price_rebate_money_order_float / 10) * 10)
            else:
                price_rebate_money_order = ""
        except (ValueError, TypeError):
            price_rebate_money_order = price_rebate_money_order_str
        
        # Sadece ürün linkini al, resim linkini alma
        product_link = item.find('productLink').text if item.find('productLink') is not None else ""
        
                        # Tüm fiyat bilgilerini birleştir
        item_info = (stock_amount, price, product_link, price_eft, price_rebate, price_rebate_money_order)
    else:
        # Stokta olmayan ürünler için sadece stok durumunu belirt, fiyat ve link bilgisi verme
        item_info = (stock_amount, "", "", "", "", "")
    
    products.append((name, item_info, full_name))

# Hugging Face token
hfapi = os.getenv("hfapi")
if not hfapi:
    raise ValueError("hfapi ortam değişkeni ayarlanmamış!")

create_repo("BF", token=hfapi, repo_type="space", space_sdk="gradio", exist_ok=True)

global_chat_history = []  # Tüm sohbet geçmişi
history_lock = threading.Lock()  # Global geçmiş için kilit
file_lock = threading.Lock()  # Dosya yazma için kilit
last_logged_index = 0  # Son kaydedilen mesaj indeksi

# Google Drive kimlik doğrulama (Hizmet Hesabı Secrets'tan)
SCOPES = ['https://www.googleapis.com/auth/drive.readonly']

def authenticate_google_drive():
    service_account_json = os.getenv("SERVICE_ACCOUNT_JSON")
    if not service_account_json:
        raise ValueError("SERVICE_ACCOUNT_JSON ortam değişkeni bulunamadı!")
    
    try:
        json_data = json.loads(service_account_json)
        print("JSON başarıyla ayrıştırıldı:", json_data.keys())
        print("Private key içeriği:", json_data.get("private_key"))
        creds = Credentials.from_service_account_info(json_data, scopes=SCOPES)
    except json.JSONDecodeError as e:
        raise ValueError(f"Secrets'tan alınan JSON geçersiz: {e}")
    except Exception as e:
        raise ValueError(f"Kimlik doğrulama hatası: {e}")
    
    return build('drive', 'v3', credentials=creds)

def download_file_from_drive(service, file_id, file_name):
    request = service.files().get_media(fileId=file_id)
    fh = io.BytesIO()
    downloader = MediaIoBaseDownload(fh, request)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
    fh.seek(0)
    with open(file_name, 'wb') as f:
        f.write(fh.read())
    return file_name

# Dökümanları Google Drive'dan çekme ve işleme
document_content = ""
service = authenticate_google_drive()

try:
    # Google Drive'dan Excel dosyası
    excel_file_id = "test10rgiGp5y5ZYU0dpRvps2t0t1dEzjW8LK"  # Örnek Excel dosya ID'si, kendi ID'nizi ekleyin
    excel_file = download_file_from_drive(service, excel_file_id, "veriler.xlsx")
    df = pd.read_excel(excel_file)
    excel_text = df.to_string()
    document_content += excel_text + "\n"
except Exception as e:
    print(f"Google Drive Excel okuma hatası: {e}")

try:
    # Google Drive'dan Word dosyası
    word_file_id = "9I8H7G6F5E4D3C2B1A"  # Örnek Word dosya ID'si, kendi ID'nizi ekleyin
    word_file = download_file_from_drive(service, word_file_id, "aciklamalar.docx")
    doc = Document(word_file)
    word_text = "\n".join([para.text for para in doc.paragraphs])
    document_content += word_text
except Exception as e:
    print(f"Google Drive Word okuma hatası: {e}")

def run_scheduler(chat_history_ref):
    def scheduled_save_and_upload():
        global last_logged_index
        if chat_history_ref:
            print(f"Zamanlayıcı tetiklendi: {time.strftime('%Y-%m-%d %H:%M:%S')}")
            try:
                with file_lock:  # Dosya yazma kilidi
                    with open(LOG_FILE, 'a', encoding='utf-8') as f:
                        f.write("\n--- Zamanlanmış Kayıt: {} ---\n".format(time.strftime("%Y-%m-%d %H:%M:%S")))
                        with history_lock:  # Geçmiş kilidi
                            new_messages = chat_history_ref[last_logged_index:]
                            for msg in new_messages:
                                if msg["role"] in ["user", "assistant"]:
                                    f.write(f"{msg['role'].capitalize()}: {msg['content']}\n")
                            last_logged_index = len(chat_history_ref)  # Son indeksi güncelle
                    print(f"Sohbet dosyaya kaydedildi: {os.path.abspath(LOG_FILE)}")
            except Exception as e:
                print(f"Kayıt hatası: {e}")
                time.sleep(5)  # Hata sonrası tekrar denemeden önce bekle
                return  # Tekrar deneme için erken çıkış

            HF_REPO_ID = "SamiKoen/BF"
            api = HfApi(token=hfapi)
            for attempt in range(3):  # 3 kez tekrar deneme
                try:
                    with file_lock:
                        api.upload_file(
                            path_or_fileobj=LOG_FILE,
                            path_in_repo="chat_logs.txt",
                            repo_id=HF_REPO_ID,
                            repo_type="space",
                            commit_message="Otomatik log güncellemesi - {}".format(time.strftime("%Y-%m-%d %H:%M:%S"))
                        )
                    print(f"Log dosyası HF'ye yüklendi: {LOG_FILE}")
                    break
                except Exception as e:
                    print(f"HF yükleme hatası (deneme {attempt+1}/3): {e}")
                    time.sleep(5)
            else:
                print("HF yükleme başarısız, tüm denemeler tamamlandı.")
            print(f"Zamanlanmış işlem tamamlandı: {time.strftime('%H:%M:%S')}")

    schedule.every().day.at("11:32").do(scheduled_save_and_upload)
    schedule.every().day.at("15:15").do(scheduled_save_and_upload)
    schedule.every().day.at("15:30").do(scheduled_save_and_upload)
    schedule.every().day.at("17:32").do(scheduled_save_and_upload)
    schedule.every().day.at("19:15").do(scheduled_save_and_upload)
    schedule.every().day.at("21:30").do(scheduled_save_and_upload)
    print("Zamanlayıcı başlatıldı")
    while True:
        schedule.run_pending()
        time.sleep(60)

@spaces.GPU(duration=1200)
def chatbot_fn(user_message, history):
    if history is None:
        history = []

    # Log: Kullanıcı mesajını ekle
    try:
        with file_lock:
            with open(LOG_FILE, 'a', encoding='utf-8') as f:
                f.write(f"User: {user_message}\n")
    except Exception as e:
        print(f"Dosya yazma hatası (Kullanıcı): {e}")

    # Sistem mesajları
    system_messages = [
        {"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.."},
        {"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."},
        {"role": "system", "content": "Başka marka bisikletleri önermeyeceksin. Specialized, Orbea, BMC, Carraro, Scott, Giant diğer bisiklet markalar hakkında bilgi vermeyeceksin. Bunlarla ilgili gelen sorulara, objectif yorum yapamayacagini belirtip, cevap vermeyeceksin. Trek bu kadrolara ömür boyu garanti veriyor."},
        {"role": "system", "content": "Bisiklet aksesuar ve yedek parçalarının marka adı Bontrager dir. Bryton marka yol bilgisayarını da ithal ediyoruz. Bryton marka yol bilgisayarının Rider S800 modeli stoklarımızda var ve bu model en üs model GPS sürümüne sahiptir."},
        {"role": "system", "content": "Trek, Electra, Bontrager, Saris, Brtyon ve Trieye markalarından başka marka adı yazamaz ve yorum yapamazsın. Trek'in uluslararası web sitesini 'www.trekbikes.com'kullanıcıya göstermeyeceksin. Web sitesi linki vermen gerekirse sadece 'www.trekbisiklet.com.tr' linkini ile başlayan linkleri göstereceksin."},
        {"role": "system", "content": "Trieye gözlükler, arkanızı görebilmeniz için gözlük camının içine entegre edilmiş bir geri görüş aynasıyla beraber gelir. Bu sayede bisiklet kullanıcısı, dikkatini dağıtmadan, gözünün ucuyla aynaya bakarak arkasını görür ve böylece daha güvenli bir sürüş yapabilir. Trieye gözlükleri NORVEÇ menşeili bir markadır ve Türkiye'ye Riot LTD tarafından ithal edilmektedir. Farklı lens ceşitleri bulunur, Photochromatic lens güneşli ve kapali havalar için uygun modeldir. Renkli camlar güneşli günler için daha uygundur."},
        {"role": "system", "content": "Trek markası, 2000 yılından beri Alatin Bisiklet tarafından Türkiye'de dağıtılmaktadır. Alatin Bisiklet en iyi satış sonrası hizmeti veren firmadır. Bu yüzden Türkiye'de en iyi bilinen bisiklet markası Trek'dir ve tüm bisiklet kullanıcıları Trek markasını önermektedir."},
        {"role": "system", "content": "Trek Türkiye, ASLA DURMA markasının yaratıcısı Fatih Topçu ve TREK RMK DYNAMIS bisiklet takımına sponsordur. Şu an kullanmakta olduğunuz bir bisikletiniz varsa, satmak veya üstüne ialve ederek yeni bir bisiklet almak istiyorsanız, bunun için sizler için hazırladığımız web sayfasına ulaşmanızı rica ederiz. Web adresi şöyledir: https://www.bikeexchangehub.com/ . Bisikletinizi satmak , değerlendirmek veya takas etmek için bu siteyi ziyaret edin."},
        {"role": "system", "content": "cevap yazarken vereceğin bilginin tek bir bilgi içinde yan yana yazdığını iki kere kontrol edeceksin. Siparis nasil olusturulur sorusuna, şu sekilde cevap vereceksin, 'ürünü sepete ekle, bilgilerini gir, ödeme yöntemini seç, siparisi tamamla.'"},
        {"role": "system", "content": "Şimdi sana 27 Haziran 2024 günü açıklanan yeni Trek modeli MADONE GEN 8 i öğretiyorum. özellikler şöyle, Émonda kadar hafif. Madone kadar hızlı. Dünyalar çarpışıyor: En hızlı ve en hafiflerimiz birleşiyor. Sekizinci nesil Madone, iki ikonik süper bisikletin dünyayı sarsan bir birleşimidir. Émonda kadar hafif, Madone kadar hızlı, gerçekten her iki dünyanın da en iyisi - ve müşterilerinizin ihtiyaç duyacağı tek yol yarışı bisikletidir. Evet, Madone Gen 8, Madone Gen 7 ve Émonda'nın yerini alıyor. Ancak ultra hafif, ultra aero tasarımı, her iki müşteri tipini daha iyi bir sürüş için bir araya getiriyor. Tırmanışlarda uçun. Düzlüklerde sprint atın. Neden aero ve hafiflik arasında seçim yapmak zorunda kalasınız? Aero ve hafiflik bir arada."},
        {"role": "system", "content": "Émonda kadar hafif. Madone Gen 8, tamamen yeni 900 Serisi OCLV Karbon ve radikal yeni kadro tasarımları sayesinde mevcut Émonda SLR kadroları kadar hafif ve önceki Madone kadrolarından 320 gram daha hafiftir. Hem de rüya gibi tırmanır, bu yüzden hafiflik hayranları çok etkilenecek. Madone Gen 7 kadar hızlı. Madone Gen 8, Émonda'dan saatte 77 saniye daha hızlıdır ve hızın nasıl göründüğünü yeniden tanımlayan yeni Full System Foil aero şekilleriyle önceki Madone kadar hızlıdır. Ayrıca, yeni RSL Aero suluklar ve Kafesler (Madone SLR ile birlikte verilir) tüm sistemini her bisiklette daha hızlı hale getirmeye yardımcı olur. Daha hafif ve daha konforlu IsoFlow teknolojisi. Madone Gen 8'in güncellenmiş IsoFlow teknolojisi eskisinden daha hafif ve dikey olarak %80'e kadar daha uyumludur, yol titreşimlerini emerek sürücülerin daha uzun süre daha güçlü sürüş yapmasını sağlar. Yeni daha hafif ve daha ergonomik gidon. Madone SLR modelleri, son derece hafif OCLV Karbondan üretilen güncellenmiş Aero RSL Yol Entegre gidonu, ultra hızlı aero şekilleri ile sürücüleri en üst aero pozisyonuna getirir. En iyi fiyata en iyi yarış bisikleti. Tamamen yeni Madone SL, SLR modeliyle aynı devrim niteliğindeki kadro teknolojisini paylaşıyor, ancak daha ekonomik 500 Serisi OCLV Karbon ve iki parçalı gidon/boğaz kombinasyonları ile maliyetleri düşürüyor. SL modelleri ayrıca mekanik vites uyumludur. Aero ve Hafiflik arasında seçim yapmanıza gerek yok. Artık hızlı ve hafif, sprint veya tırmanış arasında seçim yapmak zorunda değil. Madone Gen 8 ile Émonda müşterileri daha da aerodinamik bir sürüşe, Madone müşterileri ise her zamankinden daha hızlı tırmanmalarına yardımcı olan ultra hafif bir bisiklete sahip oluyor."},
        {"role": "system", "content": "Sen aynı zamanda bir Bıke Finder asistanısın. Görevin insanların doğru bisiklet modeli seçimini sağlamak. Kişilere sorucağın bir takım sorularala, kişileri tanıyıp, onlara stoklarımızda bulunan bisikletlerin model, boylarını tavsiye edeceksin."},
        {"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!"  },
        {"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."},
        {"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"}
    ]

    # Döküman verilerini sistem mesajlarına ekle
    if document_content:
        system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content}"})

    # Kullanıcı mesajında ürün ismi geçiyorsa ekle
    input_words = user_message.lower().split()
    for product_info in products:
        if product_info[0] in input_words:
            if product_info[1][0] == "stokta":
                # Kampanyalı fiyat kontrolü
                has_campaign = product_info[1][4] and product_info[1][4] != ""
                
                # Orijinal fiyat her zaman gösterilir
                normal_price = f"Orijinal fiyat: {product_info[1][1]} TL"
                
                # Kampanyalı fiyat bilgisi
                rebate_price = ""
                discount_info = ""
                eft_price = ""
                rebate_money_order_price = ""
                
                if has_campaign:
                    # Ürün kampanyalıysa, kampanyalı fiyat gösterilir
                    rebate_price = f"\nKampanyalı fiyat: {product_info[1][4]} TL"
                    
                    # İndirim miktarı hesaplanır
                    try:
                        original_price = float(product_info[1][1].replace(',', '.'))
                        campaign_price = float(product_info[1][4].replace(',', '.'))
                        discount_amount = original_price - campaign_price
                        
                        # İndirim miktarı 0'dan büyükse göster
                        if discount_amount > 0:
                            # İndirim miktarı için yuvarlama kurallarını uygula
                            if discount_amount > 200000:
                                discount_amount_rounded = round(discount_amount / 5000) * 5000
                            elif discount_amount > 30000:
                                discount_amount_rounded = round(discount_amount / 1000) * 1000
                            elif discount_amount > 10000:
                                discount_amount_rounded = round(discount_amount / 100) * 100
                            else:
                                discount_amount_rounded = round(discount_amount / 10) * 10
                                
                            # İndirim bilgisi
                            discount_info = f"\nYapılan indirim: {discount_amount_rounded:.0f} TL"
                    except (ValueError, TypeError):
                        discount_info = ""
                    
                    # Havale indirimli kampanyalı fiyat bilgisini gösterme
                    rebate_money_order_price = ""
                else:
                    # Kampanyalı değilse, havale indirimli normal fiyatı göster
                    if product_info[1][3] and product_info[1][3] != "":
                        eft_price = f"\nHavale indirimli fiyat: {product_info[1][3]} TL"
                
                # Ürün linki
                product_link = f"\nÜrün linki: {product_info[1][2]}"
                
                # Tüm bilgileri birleştir
                new_msg = f"{product_info[2]} {product_info[1][0]}\n{normal_price}{rebate_price}{discount_info}{eft_price}{rebate_money_order_price}{product_link}"
            else:
                # Ürün stokta yoksa sadece stok durumunu bildir
                new_msg = f"{product_info[2]} {product_info[1][0]}"
            system_messages.append({"role": "system", "content": new_msg})

    messages = system_messages + history + [{"role": "user", "content": user_message}]

    payload = {
        "model": "gpt-4.1",
        "messages": messages,
        "temperature": 0.2,
        "top_p": 1,
        "n": 1,
        "stream": True,
        "presence_penalty": 0,
        "frequency_penalty": 0,
    }
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {OPENAI_API_KEY}"
    }
    
    response = requests.post(API_URL, headers=headers, json=payload, stream=True)
    if response.status_code != 200:
        yield "Bir hata oluştu."
        return

    partial_response = ""
    
    for chunk in response.iter_lines():
        if not chunk:
            continue
        chunk_str = chunk.decode('utf-8')
        if chunk_str.startswith("data: ") and chunk_str != "data: [DONE]":
            try:
                chunk_data = json.loads(chunk_str[6:])
                delta = chunk_data['choices'][0]['delta']
                if 'content' in delta:
                    partial_response += delta['content']
                    yield partial_response
            except json.JSONDecodeError as e:
                print(f"JSON parse hatası: {e} - Chunk: {chunk_str}")
        elif chunk_str == "data: [DONE]":
            break

    # Log: Asistan cevabını ekle
    try:
        with file_lock:
            with open(LOG_FILE, 'a', encoding='utf-8') as f:
                f.write(f"Bot: {partial_response}\n")
    except Exception as e:
        print(f"Dosya yazma hatası (Bot): {e}")

    # Global geçmişi güncelle
    with history_lock:
        global_chat_history.append({"role": "user", "content": user_message})
        global_chat_history.append({"role": "assistant", "content": partial_response})

# Slow echo (test için)
def slow_echo(message, history):
    for i in range(len(message)):
        time.sleep(0.05)
        yield "You typed: " + message[: i + 1]

# Kullanım modu
USE_SLOW_ECHO = False
chat_fn = slow_echo if USE_SLOW_ECHO else chatbot_fn

if not USE_SLOW_ECHO:
    scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
    scheduler_thread.start()
    
# Trek markasına özel tema oluştur (düzeltilmiş sürüm)
trek_theme = gr.themes.Base(
    primary_hue="red",  # Trek kırmızısı için
    secondary_hue="slate",  # Gri tonları için
    neutral_hue="slate",
    radius_size=gr.themes.sizes.radius_sm,  # Köşe yuvarlatma değerleri
    spacing_size=gr.themes.sizes.spacing_md,  # Aralık değerleri
    text_size=gr.themes.sizes.text_sm  # Yazı boyutları (small)
)
# Chatbot kartları için arka plan renkleri değiştiren CSS
custom_css = """
/* Genel font ayarları */
.gradio-container, .gradio-container *, .message-wrap, .message-wrap p, .message-wrap div, 
button, input, select, textarea {
    font-family: 'Segoe UI', 'SF Pro Text', 'Roboto', -apple-system, BlinkMacSystemFont, 
                 'Helvetica Neue', Arial, sans-serif !important;
    font-size: 0.6rem !important;
}

/* Input alanı için de aynı boyut */
.message-textbox textarea {
    font-size: 0.6rem !important;
}

/* Başlıklar için özel boyutlar */
h1 {
    font-size: 1.4rem !important;
    font-weight: 800 !important;
}

h2 {
    font-size: 1.2rem !important;
    font-weight: 600 !important;
}

h3 {
    font-size: 1rem !important;
    font-weight: 600 !important;
}

/* Kart arka plan renkleri - görseldeki gibi */
/* Kullanıcı mesajları için mavi tonda arka plan */
.user-message, .user-message-highlighted {
    background-color: #e9f5fe !important;
    border-bottom-right-radius: 0 !important;
    box-shadow: 0 1px 2px rgba(0,0,0,0.1) !important;
}

/* Bot mesajları için beyaz arka plan ve hafif kenarlık */
.bot-message, .bot-message-highlighted {
    background-color: white !important;
    border: 1px solid #e0e0e0 !important;
    border-bottom-left-radius: 0 !important;
    box-shadow: 0 1px 2px rgba(0,0,0,0.05) !important;
}

/* Mesaj baloncuklarının köşe yuvarlatma değerleri */
.message-wrap {
    border-radius: 12px !important;
    margin: 0.5rem 0 !important;
    max-width: 90% !important;
}

/* Sohbet alanının genel arka planı */
.chat-container, .gradio-container {
    background-color: #f7f7f7 !important;
}

/* Daha net yazılar için text rendering */
* {
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

/* JavaScript ile placeholder eklemek için */
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Textbox'u bul
    setTimeout(function() {
        const textareas = document.querySelectorAll('textarea');
        textareas.forEach(textarea => {
            textarea.placeholder = "Sorunuzu buraya yazın... (örn: Kadromun boyu ne olmalı?)";
            
            // Placeholder stillemesi
            textarea.style.fontStyle = "italic";
            textarea.style.color = "#999";
        });
    }, 1000); // Sayfa yüklendiğinde biraz bekleyerek elementlerin oluşmasını sağla
});
</script>
<style>
"""


# Demo arayüzüne CSS'i ekleyin
demo = gr.ChatInterface(
    fn=chat_fn,
    title="Trek Asistanı",
    theme="soft",
    type="messages",
    flagging_mode="manual",
    flagging_options=["Doğru", "Yanlış", "Emin değilim", "Diğer"],
    save_history=True,
    autoscroll=True,
    css=custom_css
)

if __name__ == "__main__":
    demo.launch(debug=True)