Skip to content
Berk2259Public

About

Admin panelden yönetilen, çok müşterili ve kategorili fiyat takip sistemi. Fiyat değişimlerinde Telegram üzerinden bildirim gönderir. Panel Next.js, bot Python ile yazıldı, veritabanı Supabase kullanıyor.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Cheapo

Admin panelden yönetilen, çok müşterili ve kategorili fiyat takip sistemi. Fiyat değişimlerinde müşterilere Telegram bildirimi gider. Ürünün adı "Cheapo", logosu web/public/logo.png (site içinde Logo bileşeni ile kullanılır); repo ve klasör adı geriye dönük uyumluluk için Price_Tracker_Bot olarak kalıyor.

Klasör yapısı

Price_Tracker_Bot/
├── bot/
│   ├── main.py        Telegram botu (müşteri bağlama)
│   ├── checker.py     Fiyat kontrolcüsü (fiyatı okur ve kaydeder)
│   ├── notifier.py    Fiyat değişiminde Telegram bildirimi
│   ├── adapters/      Fiyat okuyucular (json_ld.py: düz HTTP; browser.py: gerçek tarayıcı, sırayla json_ld/next_data/data_testid/price_class dener)
│   ├── requirements.txt
│   └── .env           Gizli anahtarlar (GitHub'a gitmez)
├── web/
│   ├── src/app/admin/     Admin sayfaları (yeni kayıtlar, müşteri talepleri, Premium talepleri, hesap kaldırma talepleri, müşteriler, kategoriler, kaynaklar, ürünler,takipler, fiyat geçmişi, bildirimler)
│   ├── src/app/login/     Tek giriş sayfası (admin ve müşteri için)
│   ├── src/app/portal/    Müşteri portalı (ürünlerim, bildirimler, talepler, plan, rapor, kıyas, alışveriş listesi)
│   ├── src/app/page.tsx   Herkese açık tanıtım sayfası (landing page)
│   ├── src/lib/supabase/  Supabase bağlantıları (tarayıcı ve sunucu)
│   ├── src/proxy.ts       Giriş koruması
│   ├── src/components/    Ortak bileşenler (yan menü, butonlar, logo)
│   ├── src/components/landing/  Landing page bölümleri (hero, 3 adımda hazır, kategoriler, SSS, planlar)
│   └── .env.local         Panel ayarları (GitHub'a gitmez)
└── supabase/          Veritabanı şema dosyaları (SQL)

Teknolojiler

Parça Seçim
Veritabanı ve giriş Supabase
Admin panel Next.js
Bot Python 3.14
Fiyat okuma httpx (düz HTTP), Playwright (gerçek tarayıcı)
Bildirim Telegram Bot API

Sistem nasıl çalışır

Admin panel ──► Supabase ◄── Bot ──► Telegram
 (yönetim)     (veritabanı)  (fiyat    (müşteriye
                              çeker)   bildirim)
  1. Admin, panelden müşteri, kategori ve takip edilecek ürünleri tanımlar.
  2. Bot, Supabase'den aktif ürünleri okur ve her ürünün fiyatını kaynağından çeker.
  3. Fiyat geçmişe kaydedilir. Fiyat değiştiyse ürünü takip eden müşterilere Telegram'dan bildirim gider.

Bir ürünün fiyatı müşteri başına değil, ürün başına bir kez çekilir. Aynı ürünü birden çok müşteri takip etse de kaynağa tek istek gider.

Her ürünün kendi kontrol aralığı vardır (check_interval_minutes). Kontrolcü ne kadar sık çalıştırılırsa çalıştırılsın, yalnızca aralığı dolan ürünlere bakar.

Kontrol aralığı en az 5 dakikadır. Bir ürünün linki değiştirilirse eski fiyat sıfırlanır, böylece bot yeni sayfadaki fiyatı eski ürünle kıyaslayıp yanlış bildirim göndermez.

Aynı turda birden fazla ürünün kontrol zamanı gelmişse, ürünler art arda gecikmesiz çekilmez; her ürün arasında 2-5 saniye rastgele bekleme vardır (checker.py). Bu, aynı siteye kısa sürede çok sayıda istek gidip bot koruması tarafından engellenme riskini azaltır.

Admin panel /admin altındadır (/admin/login hariç, girişsiz erişilemez). Kök adres (/) herkese açık tanıtım sayfasıdır (landing page).

Bildirim kuralları

Bir ürünün fiyatı bir önceki kontrole göre değiştiğinde, ürünü takip eden her müşteri için şu kurallara bakılır:

  • Her değişimde bildir seçiliyse: her fiyat değişiminde bildirim gider.
  • Hedef fiyat girilmişse: fiyat hedefin üstündeyken hedefe ya da altına inerse bildirim gider. Fiyat hedefin altında kaldığı sürece tekrar bildirim gitmez.

Ürünün ilk kez okunduğunda (önceki fiyat yokken) bildirim gitmez. Gönderilen her bildirim notification_log tablosuna kaydedilir.

Bir müşteriye bildirim gönderimi başarısız olursa (örn. geçersiz/erişilemeyen Telegram chat ID), bu hata sadece o müşteri için loglanır; aynı turdaki diğer müşterilere giden bildirimleri durdurmaz (notifier.py).

Müşteri bağlama

Her müşterinin panelde otomatik üretilen bir bağlama kodu vardır. Panelden müşteri için bağlama linki kopyalanıp müşteriye gönderilir. Müşteri linke tıklayıp Start'a bastığında (bot /start KOD komutunu alır) bot Telegram chat ID'sini o müşteriye kaydeder.

Panel erişimi

Panele yalnızca tek bir admin hesabı girebilir. Giriş Supabase Auth ile yapılır. Veritabanında her tabloda satır bazlı güvenlik (RLS) açıktır ve kurallar is_admin() fonksiyonuna bağlıdır; bu fonksiyon yalnızca admin hesabının kimliğini (UID) kabul eder. Başka bir hesap açılsa bile hiçbir veri görülemez. Bot ise service_role ile çalışır ve bu kuralları atlar.

Giriş tek bir sayfadan (/login) yapılır. Yönlendirme mantığı proxy.ts'te: giriş yapan hesabın customers tablosunda kaydı varsa müşteri portalına (/portal) gider. Kaydı yoksa, e-postası ADMIN_EMAIL ortam değişkenindeki adresle eşleşiyorsa admin panele (/admin) gider; eşleşmiyorsa (ne müşteri kaydı ne admin e-postası) sistem bu hesabı güvenlik gereği otomatik oturumdan çıkarır ve /login'e geri gönderir. Bu kontrol, kendi kendine kayıt (/signup) açıldıktan sonra eklendi: daha önce "müşteri kaydı yoksa admindir" varsayımı vardı, ki tek admin hesabı dışında kimsenin auth hesabı olmadığı dönemde güvenliydi; ama başarısız/yarım kalan bir kayıt denemesi (auth hesabı oluşur ama customers satırı oluşmazsa) o kullanıcıyı yanlışlıkla admin gibi gösterebilirdi — artık böyle bir hesap gerçek admin verisini göremez (RLS zaten engeller) ama artık admin paneline hiç yönlendirilmez de, direkt atılır. Her istekte bu kontrol tekrar yapılır, yani bir müşteri adres çubuğuna elle /admin yazsa da otomatik olarak /portal'a geri gönderilir (ve tersi). Müşteri çöp kutusuna atılmışsa (customers.deleted_at dolu) bu kontrol "kayıt yok" saymaz; oturumu kapatılıp /login'e "hesap kaldırılmış" mesajıyla geri gönderilir.

Giriş sayfası koyu temalı, ortada bölünmüş kartlı bir tasarıma sahiptir (components/login-showcase.tsx, components/login-showcase-data.ts). Arka planda örnek ürün/fiyat kartları çapraz şeritler halinde sürekli akar (saf CSS animasyonu, globals.css içindeki lg- ön ekli kurallar); bu kartlardaki ürün/fiyat bilgileri sayfa herkese açık olduğu için tamamen kurgusal örnek verilerdir, gerçek müşteri verisi değildir. Sağdaki form e-posta/şifre alanları, şifre göster-gizle, "Beni hatırla" (şu an yalnızca görsel, kalıcı bir davranışı yok) ve "Şifremi unuttum" (henüz pasif, ileride eklenecek) içerir.

Kendi kendine kayıt

Ziyaretçi landing page'deki "Kayıt ol" butonuyla (/signup) ad, e-posta ve şifresini girip hesabını anında açar; admin onayı gerekmez. Sunucu tarafında (app/actions.ts → signUpCustomer) önce Supabase Auth'ta kullanıcı oluşturulur (email_confirm: true, doğrulama maili beklenmez), sonra customers tablosunda plan: "free" ile bir kayıt açılır, ardından otomatik oturum açılıp /portal'a yönlendirilir. İki adım da service_role ile (RLS'i atlayarak) yapılır çünkü bu noktada henüz bir oturum yoktur; adım 2 başarısız olursa adım 1'de açılan auth hesabı geri silinir (yetim hesap kalmasın diye).

Önceden bu akış "talep bırak, admin elle hesap açsın" şeklindeydi (leads tablosu + Talepler sayfası); bu sistem tamamen kaldırıldı. Premium'a geçmek isteyen müşteri önce Ücretsiz kaydolur, sonra portaldan (Planım sayfası) Premium talebi gönderir — aşağıya bakın.

Müşteri portalı

Hesabı açılan müşteri /login'den giriş yapıp /portal'a yönlenir. Portal koyu temalıdır, masaüstünde yan menü, telefonda alt sekmelerle çalışır (components/portal-shell.tsx). Sayfalar:

  • Ürünlerim: özet kutuları, hedefinin altına inen ürün için fırsat bandı, ürün kartları (fiyat grafiği, en düşük/en yüksek fiyat, değişim yüzdesi, hedefe yakınlık, favori yıldızı), arama, sıralama ve kart/liste görünümü. Sağ kolonda plan kartı, Telegram durumu ve son 3 bildirim.
  • Favorilerim: "Ürünlerim"deki bir kartın yıldızına basılan ürünler burada toplanır (subscriptions.is_favorite). Ayrı, hafif bir takip katmanı değildir; sadece zaten takip edilen ürünler arasında hızlı erişim sağlar, bildirim ayarını etkilemez.
  • Bildirimlerim: Telegram'dan gelen bildirimlerin günlere göre kaydı.
  • Talep gönder: kategori ve ürün seçerek talep. Aynı isimli ürünleri farklı marketlerde ayırt edebilmek için her ürünün yanında hangi marketten olduğu görünür. Seçilen her ürün için müşteri kendi bildirim tercihini belirler ("Her değişimde bildir" ve/veya hedef fiyat); en az biri seçilmeden talep gönderilemez. Plan sınırı seçim sırasında önizlenir ve aşılırsa gönderilmeden uyarılır. Taleplerin durumu adım adım izlenir (Gönderildi, İnceleniyor, Takibe eklendi).
  • Planım: Ücretsiz ve Premium planın karşılaştırması.
  • Hesap ayarları: profil adı, şifre değiştirme (Supabase Auth üzerinden, mevcut şifre doğrulanır), Telegram bağlantısını kesme, ve "hesabı kaldır" talebi gönderme (customers.removal_requested_at; admin panelde Hesap kaldırma talepleri sayfasından görür, onaylarsa hesap normal "sil" akışıyla çöp kutusuna atılır, reddederse talep temizlenir).
  • Yardım & SSS: üstte üç hızlı rehber kartı (ilk ürünü takibe alma, Telegram bağlama, ilk sepeti oluşturma; karta tıklayınca adımlar açılır), altında kısa sorular (akordiyon). İçerik components/portal-help.tsx içinde sabit metindir, veritabanına bağlı değildir.
  • Alışveriş listesi (yalnızca Premium): market ürünlerinden sepet oluşturma ve satıcı bazında toplam kıyaslama.
  • Haftalık rapor ve Ürün kıyası (yalnızca Premium): Ücretsiz müşteri kilitli önizleme görür.

Haftalık rapor, price_daily görünümündeki günlük son fiyatlardan ve bildirim kayıtlarından hesaplanır (son 7 ya da 30 gün); ürün listesinde ve seçili ürün detayında kategorinin yanında hangi mağazadan takip edildiği de gösterilir. Ürün kıyası, aynı ürünün farklı satıcılardaki fiyatını yan yana gösterir: yönetici, aynı ürünün her satıcıdaki kaydını admin panelinde ürün düzenlerken aynı karşılaştırma grubuna (products.comparison_group, örn. coca-cola-1-5l) koyar. Müşteri bir ürünü takip ettiğinde, o ürünün grubundaki tüm satıcılar kıyas sayfasında görünür. Karşılaştırma grupları, içindeki ürünlerin kategorisine göre (Market, Elektronik, Giyim vb.) ayrı başlıklar altında gruplanır; üstte her kategoriye hızlıca kaydıran butonlar vardır (birden fazla kategori olduğunda anlamlı, tek kategoride de başlık yine görünür). Her kategori bölümü kendi içinde bağımsız sayfalanır (5 ürün grubu/sayfa), çok sayıda karşılaştırma grubu birikse de sayfa uzayıp gitmez. "Destek al" ve "Premium için yaz" düğmeleri (hem portal ana sayfasındaki plan kartında hem Planım sayfasında) Gmail'in web compose linkine (https://mail.google.com/mail/?view=cm&fs=1&to=norelsoft@gmail.com) yeni sekmede açılır. mailto: yerine bu yöntem seçildi çünkü kullanıcının bilgisayarında varsayılan bir masaüstü mail uygulaması kurulu olmayabilir; bu durumda mailto: linki sessizce hiçbir şey yapmaz.

Alışveriş listesi tek bir sepetten ibaret değildir: müşteri birden fazla sepet oluşturabilir (basket_lists tablosu: customer_id, name, category_id), her sepetin bir sektörü (kategorisi) vardır ve "Ürün ekle" paneli o sepette sadece aynı sektördeki takip edilen ürünleri önerir. Sepetler üstteki şeritte sekme gibi durur; sekmenin ⋮ menüsünden yeniden adlandırılabilir ya da silinebilir (silinince içindeki ürünler de gider). Bir sepetin içindeki ürünler basket_items tablosunda tutulur (list_id + product_id); "Ürün ekle" panelinden eklenmeyen hiçbir ürün listede görünmez. Eklenen ürün bir karşılaştırma grubuna bağlıysa, o gruptaki diğer satıcıların fiyatları da otomatik olarak aynı satırda gösterilir ve satıcı bazında toplam hesaplanıp en ucuz satıcı vurgulanır; tek satıcıdan takip edilen ürünler de eklenebilir, sadece kıyaslama çıkmaz.

Farklı marka ama aynı ihtiyacı karşılayan ürünler (örn. bir markette Banvit Tavuk Göğsü, başka markette Erpiliç Tavuk But) admin panelinde aynı karşılaştırma grubuna konamaz, çünkü gerçekte aynı ürün değiller. Bunun için müşteri, sepetindeki iki veya daha fazla ürünü elle "eşdeğer" olarak eşleştirebilir: "Eşdeğer olarak eşleştir" ile seçim moduna girer, ürünleri işaretler, bir grup ismi verir (basket_matches tablosu: customer_id, name; basket_items.match_id bu gruba bağlanır). Eşleştirilen ürünler tek satırda, her birinin hangi markette hangi ürün olduğu küçük bir alt yazıyla birlikte gösterilir; eşleştirme istendiği an geri alınabilir.

Portalın veritabanı erişimi satır bazlı güvenlik (RLS) kurallarıyla sağlanır: müşteri sadece kendi customers, subscriptions, customer_requests, notification_log ve takip ettiği ürünlerin price_history satırlarını okuyabilir; ayrıca aktif ürünleri ve aktif kaynakları okuyabilir. Çoğu tabloya yazma yetkisi yoktur (talep göndermek sunucu işleminden geçer). Tek istisna customers tablosu: müşteri kendi satırında sadece name, telegram_chat_id ve removal_requested_at sütunlarını güncelleyebilir (RLS satır kuralı + sütun bazlı grant) — profil adı, Telegram bağlantısını kesme ve hesap kaldırma talebi bu sayede çalışır. plan, deleted_at gibi hassas alanlara müşteri elle yazamaz.

Müşteri talebi ve otomatik takip

Müşteri portaldan bir kategori ve o kategorideki ürünleri seçip talep gönderir (customer_requests + customer_request_products); her ürün için kendi bildirim tercihini de (hedef fiyat ve/veya "her değişimde bildir") aynı anda belirler, bu tercihler customer_request_products tablosunda (target_price, notify_on_any_change) saklanır. Admin, panelin Müşteri talepleri sayfasından bu talepleri görür ve durumunu değiştirir (Bekliyor / İnceleniyor / Tamamlandı / Reddedildi). Durum Tamamlandı yapıldığında seçilen ürünler, müşterinin talep sırasında belirlediği hedef fiyat ve bildirim tercihiyle birlikte otomatik olarak müşterinin takiplerine (subscriptions) eklenir; admin'in ayrıca Takipler sayfasından ayarlamasına gerek kalmaz (isterse yine değiştirebilir).

Plan sınırları

Ücretsiz planda en fazla 1 kategori ve 3 ürün takip edilebilir (premium'da sınır yok). Bu sınır, müşteri portaldan yeni talep gönderirken kontrol edilir; sınır aşılıyorsa talep reddedilir ve mevcut kullanım anlaşılır bir mesajla gösterilir. Sınırlar web/src/lib/plan-limits.ts dosyasında tanımlıdır. Admin, Takipler sayfasından bu sınırın üstünde elle ekleme yapabilir.

Şimdi kontrol et

Admin, Ürünler sayfasından bir ürüne "Şimdi kontrol et" diyebilir. Bu, ürünün force_check_requested bayrağını işaretler. Bot en fazla 1 dakika içinde bu bayrağı görüp ürünü hemen kontrol eder ve bayrağı sıfırlar; ürünün kendi kontrol aralığını beklemesine gerek kalmaz.

Landing page tasarımı

Kök sayfa (/) bölümlere ayrılmıştır ve her bölüm web/src/components/landing/ altında ayrı bir dosyadır: hero (cihaz sahnesi ve kayan ürün duvarı), 3 adımda hazır, kategoriler, Merak edilenler (bot sohbeti şeklinde), planlar ve talep formu. Talep formu web/src/components/lead-form.tsx dosyasındadır ve leads tablosuna kaydeder.

Renkler globals.css içindeki @theme bloğunda tanımlıdır. Animasyonlar saf CSS ile yazılmıştır ve bölüme göre önek alır: hv- (hero), hiw- (3 adımda hazır), pl- (planlar), pf- (SSS sohbeti), lf- (talep formu). Kaydırınca başlayan animasyonlar küçük bir IntersectionObserver ile tetiklenir, ayarlarda "hareketi azalt" seçiliyse tüm animasyonlar kapanır (prefers-reduced-motion).

Hero'daki cihaz ekranlarında görünen ürünler ve fiyatlar örnek veridir, gerçek takip verisi değildir.

Admin paneli tasarımı

Admin paneli koyu temalıdır ve müşteri portalından ayrıdır. web/src/app/admin/layout.tsx içindeki force-dark sınıfı, globals.css'teki özel dark varyantı sayesinde admin içindeki tüm dark: sınıflarını işletim sistemi temasından bağımsız açar; giriş sayfası ve portal etkilenmez. Renkler yine globals.css'te, .force-dark içinde gri ve yeşil paletin yeniden tanımlanmasıyla (koyu turkuaz) verilir.

  • Yan menü (sidebar.tsx): Gelenler, Katalog, Takip gruplarına ayrılmıştır. Gelenler grubu: Yeni kayıtlar, Müşteri talepleri, Premium talepleri, Hesap kaldırma talepleri. Bekleyen kayıt sayısı olanlarda (Müşteri/Premium/Hesap kaldırma talepleri — Yeni kayıtlar hariç, o bir bekleme kuyruğu değil bilgi akışıdır) kırmızı rozet görünür (sayılar admin/layout.tsx'te hesaplanır).
  • Üst çubuk ve arama (admin-topbar.tsx, admin-nav.ts): sayfa başlığı ve Ctrl+K ile açılan sayfa arama penceresi.
  • Ana sayfa: "Dikkat gerektirenler" (okunamayan ürünler, bekleyen talepler, Telegram'a bağlanmamış müşteriler), istatistik kartları, son bildirimler ve ürün durumu. Hepsi Supabase'den gerçek veriyle gelir.
  • Ürünler: arama, durum filtreleri (Hatalı, Sırada, Pasif), durum rozetleri ve tek tıkla "şimdi kontrol et". Ürün ekleme ve düzenleme sağdan açılan çekmecede yapılır (product-drawer.tsx); aynı çekmecede ürünün karşılaştırma grubu da girilir.
  • Yeni kayıtlar: kendi kendine kayıt olan müşteriler, en yeni üstte; isim, e-posta (Supabase Auth'tan service_role ile okunur, customers tablosunda e-posta tutulmaz), plan, Telegram durumu ve katılım tarihi. Arama, aktif/pasif yapma ve silme (çöp kutusuna atma) içerir.
  • Müşteri talepleri: gelen kutusu düzeni; durum filtreleri, arama ve renkli durum seçicisi.
  • Premium talepleri: müşterinin portaldaki "Planım" sayfasından gönderdiği Premium'a geçme talepleri (bıraktığı telefon/not varsa görünür). "Onayla, Premium yap" planı otomatik Premium yapar; "Reddet" talebi reddedilmiş olarak işaretler. İkisi de geçmişte kalır (silinmez), sadece bekleyen taleplerde buton görünür.
  • Hesap kaldırma talepleri: müşterilerin "Hesap ayarları"ndan gönderdiği kaldırma taleplerinin listesi, üstte arama çubuğu. Satıra tıklayınca genişler ve hesabın etkisini gösterir (takip edilen ürün sayısı, Telegram durumu, üyelik tarihi, aldığı bildirim sayısı); "Talebi reddet" (talebi temizler) ve "Hesabı kalıcı olarak sil" (normal çöp kutusu akışına atar) butonları vardır.
  • Bildirimler: günlere göre gruplanmış akış, mesajlar Telegram balonu şeklinde, müşteri seçici ve arama.
  • Fiyat geçmişi: her kaydın bir önceki kayda göre değişimi, ürün seçilince fiyat grafiği (güncel, en düşük, en yüksek, toplam değişim).
  • Kategoriler ve Kaynaklar: kart görünümü; her kartta bağlı ürün (ve kategoride müşteri) sayısı. Bağlı ürünü olan kategori ya da kaynak silinemez.
  • Takipler: müşteriye göre gruplu kartlar; her müşteride Telegram durumu (bağlı değilse bildirim gidemediği belirtilir), ürünün hangi marketten takip edildiği (etiket), güncel fiyatı ve hedefe uzaklığı. Ekleme ve düzenleme sağdan açılan çekmecede yapılır.
  • Müşteriler: kart görünümü; plan (Ücretsiz/Premium) düzenleme çekmecesinden değiştirilebilir, bağlı olmayan müşterinin bağlama linki karttan kopyalanır. Kartta kategoriler, takip sayısı ve müşterinin bildirimlerine kısayol vardır.

Tüm admin sayfaları yenilenmiştir.

Canlıya alma

Web paneli Vercel'e deploy edilir (repo mono-repo olduğu için Root Directory web seçilmelidir). Gerekli ortam değişkenleri web/.env.local ile aynıdır: NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY, TELEGRAM_BOT_USERNAME, SUPABASE_SERVICE_ROLE_KEY, ADMIN_EMAIL. master'a her push otomatik yeni bir production deploy tetikler.

Bot (bot/main.py) sürekli çalışan bir süreçtir (Telegram dinleme + 60 saniyede bir fiyat kontrolü), bu yüzden Vercel gibi serverless platformlarda çalışamaz. Sürekli ayakta kalan bir süreç çalıştırabilen ayrı bir yerde (örn. Railway, bir VPS) host edilmesi gerekir; Root Directory bot, Start Command python main.py olmalıdır.

Kurulum

Ön koşullar

  • Python 3.10 veya üstü
  • Git
  • Bir Supabase projesi
  • BotFather'dan alınmış bir Telegram bot tokenı

Bot

  1. bot/.env dosyasını oluştur (GitHub'a gitmez):
TELEGRAM_BOT_TOKEN=
SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=

Service role anahtarı çok güçlüdür: sadece botta kullanılır, panele ve GitHub'a asla konmaz.

  1. Sanal ortamı kur ve kütüphaneleri yükle:
cd bot
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txt

Botlara engel koyan siteleri (örn. Cloudflare kullananları) okumak için gerçek bir tarayıcı gerekir. Bunun için Playwright'ı ve Chromium'u bir kez kur:

pip install playwright
playwright install chromium

Bu yalnızca kaynağın yöntemi Tarayıcı (Playwright) olan ürünler için kullanılır; diğer ürünler eskisi gibi düz HTTP ile okunur.

  1. Botu çalıştır:
python main.py

python main.py çalıştığı sürece bot hem Telegram'ı dinler hem arka planda her dakika hangi ürünlerin kontrol zamanı geldiğine bakar, ayrı bir komut gerekmez. python checker.py'yi elle bir kez çalıştırmak (tek seferlik test için) hâlâ mümkündür.

Yeni bir terminal açıldığında önce sanal ortam tekrar etkinleştirilir (.venv\Scripts\Activate.ps1).

Panel

  1. web/.env.local dosyasını oluştur (GitHub'a gitmez):
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=
TELEGRAM_BOT_USERNAME=
SUPABASE_SERVICE_ROLE_KEY=
ADMIN_EMAIL=

TELEGRAM_BOT_USERNAME, müşteri bağlama linkini oluşturmak için botun kullanıcı adıdır (başında @ olmadan). ADMIN_EMAIL, admin panele kimin girebileceğini belirleyen tek e-posta adresidir (bkz. "Panel erişimi"); büyük/küçük harf ve boşluk farkı olmadan Supabase Auth'taki gerçek admin e-postanla birebir aynı olmalı.

Panelde yalnızca sb_publishable_ ile başlayan anahtar kullanılır. Service role anahtarı panele asla konmaz.

SUPABASE_SERVICE_ROLE_KEY sadece hesap açma işleminde, sunucu tarafında ("use server" dosyasında) kullanılır ve tarayıcıya asla gönderilmez. NEXT_PUBLIC_ öneki almadığından client koduna erişilemez.

  1. Paketleri kur ve çalıştır:
cd web
npm install
npm run dev

Panel http://localhost:3000 adresinde açılır.

  1. Supabase'de Authentication → Users bölümünden admin hesabını oluştur.

Veritabanı tabloları

Tablo Amaç
categories Market, E-ticaret, Uçak bileti vb.
customers Müşteriler, Telegram chat ID, bağlama kodu
customer_categories Müşteri - kategori eşleşmesi
sources Fiyat kaynağı ve çekme yöntemi (api, json_ld, http, browser)
products Takip edilen ürünler (ürün başına tek kayıt)
subscriptions Müşteri - ürün takibi, hedef fiyat
price_history Fiyat geçmişi
notification_log Gönderilen bildirim kayıtları
customer_premium_requests Portaldan gönderilen Premium'a geçme talepleri (status: bekliyor/onaylandi/reddedildi)

Ek olarak:

  • products.comparison_group: aynı grup adını taşıyan ürünler aynı ürünün farklı farklı satıcılardaki kayıtlarıdır (Ürün kıyası ve Alışveriş listesi bu alanı kullanır). Admin panelde Ürünler sayfasının liste/grup görünüm anahtarıyla bu gruplar kart halinde bir arada görülebilir.
  • basket_lists: müşterinin oluşturduğu sepetler (customer_id, name, category_id). Her sepetin bir sektörü vardır; "Ürün ekle" paneli o sepette sadece aynı sektördeki ürünleri önerir.
  • basket_items: bir sepete eklenen ürünler (list_id, product_id, match_id).
  • basket_matches: müşterinin elle "eşdeğer" olarak birleştirdiği ürün grupları (customer_id, name).
  • customers.removal_requested_at: müşteri "Hesap ayarları"ndan hesap kaldırma talebi gönderdiğinde dolar; admin panelde Hesap kaldırma talepleri sayfasından görülür, onaylanırsa hesap normal silme akışıyla çöp kutusuna atılır, reddedilirse bu alan temizlenir.
  • customer_premium_requests: müşteri aynı anda sadece bir bekleyen talebe sahip olabilir (customer_id üzerinde kısmi unique index, status = 'bekliyor' iken). Onaylanan/reddedilen talepler silinmez, geçmiş olarak tabloda kalır.
  • price_daily (görünüm): price_history'nin günlük son fiyat özeti (Türkiye saatine göre). security_invoker ile çalışır, yani sorgulayan kullanıcının RLS izinleri geçerlidir.

Fiyat çekme yaklaşımı

Her kaynağın bir okuma yöntemi vardır (sources.method, panelde Kaynaklar sayfası). Şu an iki yöntem gerçekten çalışır durumdadır; dropdown'da başka seçenek yoktur (ileride resmi API sunan bir kaynak eklenirse yeni bir yöntem olarak eklenir):

  • JSON-LD (sayfa verisi): düz HTTP isteği ile sayfadaki JSON-LD fiyat verisi okunur. Hızlıdır, ama bazı siteler JSON-LD'de güncel olmayan ya da yanlış satış kanalına ait bir fiyat verebilir (bkz. aşağıdaki not).

  • Tarayıcı (Playwright): headless Chromium ile sayfa açılır (bot/adapters/browser.py). Cloudflare gibi bot korumasını geçmek için varsayılan "HeadlessChrome" kimliği yerine normal bir Chrome kimliği kullanılır. Sayfa açıldıktan sonra fiyat, sırayla dört ayrı ayrıştırıcıyla aranır, ilk bulan kazanır — sırası bilerek "sayfada gerçekten görünen fiyat" önce, "sayfanın beyan ettiği yapısal veri" en son olacak şekildedir:

    1. CSS class'ı normalPrice/discountedPrice (adapters/price_class.py) — class adı "...normalPrice" ya da "...discountedPrice" ile biten fiyat kutusu (indirimli ürünlerde discountedPrice tercih edilir).
    2. data-testid (adapters/data_testid.py) — sayfanın düz HTML'inde data-testid="discountedPrice" etiketli fiyat kutusu.
    3. __NEXT_DATA__ (adapters/next_data.py) — Next.js sitelerinin sayfaya gömdüğü JSON içindeki fiyat.
    4. JSON-LD (adapters/json_ld.py) — sayfanın kendi Product nesnesinin offers alanı (sayfadaki rastgele bir Offer değil, özellikle bu ürüne ait olan).

    Her sayfa birkaç saniye sürer. Kontrolcü tarayıcıyı yalnızca bu yöntemdeki ürünler için başlatır. Hangi alt yöntemin tuttuğu ürünün last_status alanına yazılır ("ok · next_data" gibi) ve panelde iki yerde görünür: admin Ürünler sayfasındaki Yöntem sütununda (ürün bazında, boşsa JSON-LD ile okunmuş demektir) ve Kaynaklar sayfasındaki kart üzerinde renkli bir rozet olarak (o kaynağın ürünlerinden en az biri hangi alt yöntemle okunduysa). Aynı kaynağın farklı ürünleri, sayfa şablonları farklıysa, birden fazla alt yöntemle okunabilir — bu bir hata değildir.

    Neden JSON-LD en sona alındı: Bazı sitelerde JSON-LD'nin offers.price alanının sayfada gerçekten görünen fiyattan farklı (başka bir satış kanalına ait olabilecek) bir değer verdiği görüldü. JSON-LD hatasız, geçerli bir fiyatla "başarılı" döndüğü için hata fırlatmıyor ve döngü orada duruyordu — yani sıralamada önce olsaydı, DOM'daki doğru fiyata hiç sıra gelmezdi. Bu yüzden önce sayfada fiilen görüneni (CSS kutusu / data-testid) deniyoruz, JSON-LD sadece hiçbiri bulunamazsa son çare olarak kullanılıyor.

Yeni bir kaynak eklerken önce düz HTTP ile (JSON-LD) denenmelidir; site 403 veriyorsa kaynağın yöntemi "Tarayıcı (Playwright)" yapılır — bu durumda dört alt yöntemden hangisinin uyduğunu bot kendisi bulur, elle seçim yapılmaz. Sunucunun IP adresi bot koruması tarafından farklı değerlendirilebileceği için, canlıya alırken tarayıcı yöntemi orada da denenmelidir. Bazı siteler ürün sayfasını teslimat adresine göre kişiselleştirir; adres seçilmemiş bir oturumdan bazı ürünler gerçekte satışta olsa da 404 dönebilir — bu bot hatası değildir, ilgili ürün admin panelden pasif yapılabilir.

Çöp kutusu (soft delete)

Admin panelde bir kayıt silindiğinde veritabanından hemen kaldırılmaz; deleted_at sütunu doldurulur ("çöpe atılır") ve normal listelerden kaybolur. Gerçek silme yalnızca Çöp kutusu sayfasından "Kalıcı olarak sil" ile, onay istendikten sonra yapılır. Kapsam: products, sources, categories, customers, subscriptions, notification_log, price_history, customer_requests — panelde silme işlemi olan her tablo.

Genel action'lar tek dosyada toplanmıştır (app/admin/trash/actions.ts): softDelete, restoreFromTrash, permanentlyDelete; tablo adı parametre olarak verilir. Mevcut "sil" butonlarının fonksiyon imzaları değişmedi, sadece içleri artık gerçek delete yerine deleted_at güncelliyor. Çöp kutusu sayfası (app/admin/trash/page.tsx) bu 9 tablodan deleted_at dolu olan kayıtları tek bir listede toplar; arama ve tabloya göre filtre içerir. Sol menüdeki "Çöp kutusu" rozeti, çöpteki toplam kayıt sayısını gösterir.

Bunun güvenli çalışması için birkaç yer özellikle düzeltildi:

  • Admin panelindeki tüm ana listeleme sorgularına (Ürünler, Kaynaklar, Kategoriler, Müşteriler, Takipler, Talepler, Bildirimler, Fiyat geçmişi, Müşteri talepleri) deleted_at is null filtresi eklendi.
  • price_daily görünümü de çöpteki fiyat kayıtlarını hesaba katmayacak şekilde güncellendi (haftalık/aylık rapor bunu kullanıyor).
  • Bot: çöpe atılmış bir ürün artık kontrol edilmiyor (checker.py), çöpe atılmış bir aboneliğe/müşteriye bildirim gitmiyor (notifier.py), çöpe atılmış bir müşteri Telegram'ını bağlayamıyor (main.py).
  • Giriş mantığı: "müşteri kaydı yoksa admindir" varsayımı yüzünden, çöpe atılmış bir müşteri basitçe filtrelenirse yanlışlıkla admin paneline yönlendirilebilirdi. Bunun yerine proxy.ts (asıl güvenlik sınırı) ve login/page.tsx, müşteri bulunduğu ama çöpe atılmış olduğu durumu ayrıca kontrol eder: oturum kapatılır, /login'e "hesap kaldırılmış" mesajıyla geri gönderilir.

Not: Müşteri portalındaki (customer-facing) bazı sorgular — ör. ürün kıyası, alışveriş listesi, portal ana sayfası — henüz deleted_at filtresi almadı; bu sayfalarda çöpe atılmış bir ürün/kategori kısa süreliğine görünmeye devam edebilir. İleride ele alınacak bir sonraki adım.

Yapılanlar

  • Supabase projesi ve veritabanı şeması (8 tablo)
  • Telegram botu oluşturuldu
  • Müşteri bağlama akışı (/start KOD ile chat ID kaydı)
  • Bot -> müşteri Telegram bildirimi doğrulandı
  • Fiyat okuyucu (JSON-LD): sayfadan fiyat ve stok bilgisi çekiyor
  • Kontrolcü: aktif ürünlerin fiyatını okuyup geçmişe kaydediyor
  • Fiyat değişiminde Telegram bildirimi (her değişimde ve hedef fiyata düşünce)
  • Ürün bazlı kontrol aralığı (sırası gelmeyen ürün atlanıyor)
  • Panel iskeleti: admin girişi/çıkışı ve giriş koruması
  • Panel: yan menülü düzen ve özet kartlarıyla ana sayfa
  • Panel: müşteri yönetimi (ekleme, satır içi düzenleme, silme, Telegram bağlama linki)
  • Panel: kategori yönetimi (ekleme, satır içi düzenleme, silme)
  • Panel: kaynak yönetimi (ekleme, satır içi düzenleme, silme)
  • Panel: ürün yönetimi (ekleme, satır içi düzenleme, silme, kategori ve kaynak seçimi)
  • Panel: takip yönetimi (müşteri-ürün eşleştirme, hedef fiyat ve bildirim kuralı)
  • Panel: fiyat geçmişi (listeleme, ürüne göre filtreleme, kayıt silme)
  • Panel: bildirim kayıtları (listeleme, müşteriye göre filtreleme, kayıt silme)
  • Panel: müşteriye kategori atama (Market, E-ticaret, Uçak bileti vb.)
  • Panel: admin sayfaları /admin altına taşındı, kök adres genel kullanım için ayrıldı
  • Veritabanı: müşteri hesabı bağlantısı (auth_user_id), plan alanı, leads ve customer_requests tabloları
  • Landing page: tanıtım, plan karşılaştırması ve talep formu (leads tablosuna kaydediyor)
  • Panel: Talepler sayfası (leads listeleme, durum değiştirme, silme)
  • Panel: talepten müşteri hesabı açma (Supabase Auth + customers kaydı)
  • Tek giriş sayfası (/login), hesap türüne göre /admin veya /portal'a yönlendirme(koyu temalı, çapraz akan ürün kartlı arka plan tasarımıyla yenilendi)
  • Müşteri portalı: giriş, takip edilen ürünler ve fiyatları görme
  • Müşteri portalı: yeni talep gönderme (kategori + ürün seçimi) ve kendi taleplerini görme
  • Panel: Müşteri talepleri sayfası, Tamamlandı'da otomatik takip ekleme
  • Ücretsiz plan sınırı (1 kategori, 3 ürün), müşteri talep gönderirken kontrol ediliyor
  • Bot: Telegram hesabı başka müşteriye bağlıysa anlaşılır hata mesajı (çökmüyor)
  • Bot sürekli çalışır: Telegram dinleme ve fiyat kontrolü tek süreçte (60 saniyede bir kontrol turu)
  • Panel: "Şimdi kontrol et" düğmesi, bot en fazla 1 dakika içinde işliyor
  • Landing page yenilendi: animasyonlu hero, 3 adımda hazır, kategoriler, planlar, SSS sohbeti ve talep formu
  • Admin paneli yenilendi (1. aşama): koyu tema, gruplu menü ve rozetler, Ctrl+K arama, yeni ana sayfa, Ürünler sayfası ve düzenleme çekmecesi
  • Admin paneli yenilendi (2. aşama): Talepler, Müşteri talepleri, Bildirimler, Fiyat geçmişi (grafikli), Kategoriler ve Kaynaklar sayfaları
  • Admin paneli yenilendi (3. aşama): Takipler ve Müşteriler sayfaları, panelden müşteri planı değiştirme
  • Müşteri portalı yenilendi: koyu tema, yan menü, geniş ürün sayfası, bildirimler, gelişmiş talep formu, Planım
  • Premium: Haftalık ve aylık rapor ile Satıcılar arası ürün kıyası (aynı ürünün market kayıtları admin panelinde "Karşılaştırma grubu" ile bağlanır)
  • Premium: Alışveriş listesi (sektöre göre birden fazla sepet oluşturma/yeniden adlandırma/silme, satıcı bazında toplam kıyaslama, farklı markaları elle "eşdeğer" olarak eşleştirme)
  • Bot: gerçek tarayıcı (Playwright) ile okuma, Cloudflare korumalı siteler için; JSON-LD, NEXT_DATA, data-testid, CSS class fiyat kutusu olmak üzere 4 ayrı ayrıştırıcıyı sırayla dener
  • Panel: Ürünler sayfasında hangi ayrıştırıcının kullanıldığını gösteren "Yöntem" sütunu, liste/grup (karşılaştırma grubuna göre) görünüm anahtarı
  • Panel: uzun listelerde sayfalama (Fiyat geçmişi: sunucu taraflı, sayfa başına 25/50/100/200; Ürünler, Müşteri talepleri, Bildirimler: tarayıcı taraflı, sayfa başına seçilebilir) ve Takipler'de müşteri başına akordiyon (5'ten fazla takibi olan müşteriler varsayılan kapalı başlar)
  • Panel: Çöp kutusu (soft delete) — 9 tabloda silme artık geri yüklenebilir, kalıcı silme onay ister; bot ve giriş mantığı çöpe atılmış kayıtları görmezden gelecek şekilde güncellendi
  • Müşteri portalı: Hesap ayarları sayfası (profil adı, şifre değiştirme, Telegram bağlantısını kesme, hesap kaldırma talebi)
  • Müşteri portalı: Favorilerim sayfası (takip edilen ürünlerden yıldızlananlar)
  • Müşteri portalı: Yardım & SSS sayfası (rehber kartları + kısa SSS)
  • Panel: ürünlere karşılaştırma grubu alanı
  • Panel: Vercel'e deploy edildi (Root Directory web, gerekli ortam değişkenleri tanımlandı)
  • Panel: Talepler sayfasında hesabı zaten açılmış talepler için de silme butonu görünür hale getirildi (önceden sadece hesap açılmamış taleplerde vardı)
  • Bot: aynı turda birden fazla ürün çekilirken aralarına rastgele 2-5 saniye bekleme eklendi (bot koruması riskini azaltmak için)
  • Müşteri portalı: Haftalık rapor sayfasında ürünlerin hangi mağazadan takip edildiği gösteriliyor
  • Müşteri portalı: "Destek al" ve "Premium için yaz" düğmeleri Gmail web compose linkine bağlandı (masaüstü mail uygulaması gerektirmeden çalışır)
  • Planım sayfası ve landing page planlar bölümü: Premium özellik listesine eksik olan "Alışveriş listesi" eklendi
  • Bot: bir müşteriye bildirim gönderimi başarısız olursa diğer müşterilerin bildirimini engellemiyor (notifier.py try/except)
  • Müşteri portalı: hesap ayarlarında Telegram bağlantısını kesme/profil güncelleme için customers tablosuna sınırlı (sütun bazlı) kendi-satırını-güncelleme RLS izni eklendi
  • Müşteri portalı: Talep gönder formunda her ürün için hedef fiyat / her değişimde bildir seçimi, onaylanınca otomatik takibe aktarılıyor
  • Müşteri portalı ve admin panel: ürün listelerinde (Talep gönder, Takipler) hangi marketten olduğunu gösteren etiket eklendi
  • Ürün kıyası sayfası: karşılaştırma grupları kategoriye göre başlıklı bölümlere ayrıldı, üstte kategoriye hızlı kaydırma butonları eklendi
  • Panel: Hesap kaldırma talepleri sayfası (arama, genişleyen kartlarda hesap etkisi, talebi reddetme/hesabı silme), sol menüde bekleyen sayısı rozeti
  • Ürün kıyası sayfası: kategori bölümleri artık kendi içinde sayfalanıyor (5 ürün grubu/sayfa)
  • leads tablosu ve Talepler sayfası tamamen kaldırıldı (SQL ve kod), yerine kendi kendine kayıt geldi
  • Landing page: self-servis kayıt (/signup), navbar'da "Giriş yap" / "Kayıt ol" butonları, eski gömülü talep formu kaldırıldı
  • Güvenlik: admin panele girişi "müşteri kaydı yoksa admindir" varsayımı yerine ADMIN_EMAIL ortam değişkeniyle açıkça belirleme; eşleşmeyen/yetim hesaplar otomatik oturumdan atılıyor
  • Panel: Premium talepleri sayfası (customer_premium_requests tablosu), portaldaki Planım sayfasından telefon/not ile talep gönderme, onayla/reddet (geçmiş korunur)
  • Panel: Yeni kayıtlar sayfası (self-servis kayıt olan müşteriler, arama, aktif/pasif, silme)
  • Bot: fiyat ayrıştırıcı sırası değişti (önce DOM'daki görünen fiyat, JSON-LD en sona alındı) ve price_class/json_ld ayrıştırıcıları yanlış fiyat çekme sorununu düzeltecek şekilde güçlendirildi

About

Admin panelden yönetilen, çok müşterili ve kategorili fiyat takip sistemi. Fiyat değişimlerinde Telegram üzerinden bildirim gönderir. Panel Next.js, bot Python ile yazıldı, veritabanı Supabase kullanıyor.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages