Add backtest pipeline, betting_brain filters, score coherence + social v3
betting_brain.py: - HARD_MIN_SAMPLES=50 floor for calibrator bypass - ev_edge < 0 + >= 0.20 hard vetoes - BTTS muted (grid search found no profitable config) - Per-market optimal envelopes (MS, OU25) - Score coherence filter: main_pick must agree with score prediction - HTFT reversal cross-check for MS picks feature_builder.py / data_loader.py: - Real home/away_position from data (was hardcoded 10) - Cup detection wired into UpsetEngine - _estimate_league_position with 300-day season filter New scripts: - diagnostic_backtest.py: per-bet diagnostic backtest with loss patterns - optimize_filters.py: grid search per-market optimal thresholds - analyze_backtest_csv.py: root-cause hypothesis testing on CSV - compare_backtests.py: side-by-side validation with verdict - test_score_coherence.py: smoke test for coherence filter (20/20 pass) Reports: - diagnostic_backtest_20260525_024437 (50-match smoke) - diagnostic_backtest_20260525_035649 (1000-match in-sample) - filter_optimization_patch.json (grid search winners per market) Social poster v3: - satori + resvg HTML/CSS rendering pipeline - Twemoji football/basketball + flag SVGs - caption SEO: 12 curated hashtags per post - image SEO: descriptive filenames + .json metadata sidecar - /health, /preview-png, /run-now endpoints Docs: - mds/SESSION_HANDOFF.md: full session state for cross-machine continuity - mds/SOCIAL_POSTER_SETUP.md: API keys + test commands Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
# SESSION HANDOFF — iddaai sistem durumu
|
||||
|
||||
**Son güncelleme**: 2026-05-25 ~20:30
|
||||
**Hedef**: Başka makinede / yeni Claude session'ında bu doc tek başına okunup işin nerede kaldığı anlaşılabilmeli.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Üst-seviye hedef
|
||||
|
||||
Sistem **maç başı-1 saat** kullanıcı tetiklemesiyle çalışacak. Bahis uzmanı seviyesinde:
|
||||
- **main_pick + value_pick** (sistemin önerdiği)
|
||||
- **Tüm market olasılıkları** (MS, HT, OU05-45, BTTS, OE, DC, HTFT, HCAP, Cards, Corners)
|
||||
- **Net HT + FT skoru** + **Top-5 olası skor dağılımı**
|
||||
- **Evidence panel**: lineup impact, son 5 maç, h2h, hakem profili, benzer-oran-band geçmişi
|
||||
|
||||
Ürün modeli: hem user kendi bahisini oynar, hem sistem para kazanırsa abonelik satılır.
|
||||
Hedef ROI: **≥%10**. Günde **3-5 kaliteli bahis**.
|
||||
|
||||
Detaylı requirements doc: bu dosyanın altında, "Requirements Spec" bölümü.
|
||||
|
||||
---
|
||||
|
||||
## 🟢 Şu an arka planda KOŞAN işler
|
||||
|
||||
### 1. Validation backtest (LOCAL — bu laptop)
|
||||
- **Script**: `ai-engine/scripts/diagnostic_backtest.py`
|
||||
- **Komut**: `python scripts/diagnostic_backtest.py --start 2026-05-01 --end 2026-05-14 --max-matches 1500`
|
||||
- **Log**: `ai-engine/validation_full.log` (OneDrive senkronize)
|
||||
- **Çıkış**: bittiğinde `ai-engine/reports/diagnostic_backtest_<timestamp>.{csv,json,txt}`
|
||||
- **Tahmini bitiş**: 2026-05-25 ~22:00 (yaklaşık)
|
||||
- **Amaç**: Yeni kodla (calibrator + ev_edge veto + envelope + coherence + BTTS mute) **out-of-sample** doğrulama
|
||||
- **Risk**: Laptop uyursa ölür. Bitmesini beklemen lazım VEYA partial sonuçla devam.
|
||||
|
||||
```powershell
|
||||
# Status check (kendin)
|
||||
$log='C:\Users\fahri\OneDrive\المستندات\GitHub\iddaai\iddaai-be\ai-engine\validation_full.log'
|
||||
Select-String $log 'rate=|Outputs:' | Select-Object -Last 3 | ForEach-Object {$_.Line}
|
||||
```
|
||||
|
||||
### 2. Feeder historical scan (REMOTE — Pi server)
|
||||
- **Konum**: SSH @ haruncan@95.70.252.214:2222 → docker container `iddaai-be` → pm2
|
||||
- **PM2 process**: `feeder-historical` (id=1)
|
||||
- **Log rotation**: pm2-logrotate kurulu (max 30MB/dosya, 3 dosya, gzip)
|
||||
- **Davranış**: 2026-05-03'ten geriye 2023-06-01'e kadar mackolik'ten odds/lineup patch
|
||||
- **Otomatik restart**: 502 olunca 30 sn delay sonra restart (max 1000 kez)
|
||||
- **Beklenen süre**: 24-72 saat
|
||||
|
||||
```bash
|
||||
# Status (kendin SSH'la)
|
||||
sudo docker exec iddaai-be pm2 list
|
||||
sudo docker exec iddaai-be pm2 logs feeder-historical --lines 30 --nostream
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Bu seansta yapılan KOD değişiklikleri
|
||||
|
||||
Hepsi local repo'da, OneDrive senkronize edecek, başka makinede pull etmesen de açtığında orada olacak.
|
||||
|
||||
### A. Settlement / data layer
|
||||
| Dosya | Değişiklik |
|
||||
|---|---|
|
||||
| `iddaai-be/prisma.config.ts` | `.env` fallback ekledim (`.env.local` üstüne) — `prisma generate` çalışsın diye |
|
||||
| `iddaai-be/src/tasks/prediction-settlement.market-resolver.ts` | DC parser ayraçsız "1X/X2/12" kabul ediyor + HT_OU05/HT_OU15/HT_OU25 resolver eklendi |
|
||||
| `iddaai-be/src/tasks/feature-enrichment.task.ts` **(YENİ)** | Cron 08:15 — eksik football_ai_features row insert + odds_movement SQL backfill |
|
||||
| `iddaai-be/src/tasks/python-enrichment.task.ts` **(YENİ)** | Cron 08:25 — Python `enrich_ai_features.py` subprocess |
|
||||
| `iddaai-be/src/tasks/tasks.module.ts` | İki yeni task register |
|
||||
| `iddaai-be/src/scripts/run-feature-enrichment.ts` **(YENİ)** | Manuel one-shot trigger |
|
||||
|
||||
### B. AI engine — betting brain
|
||||
`iddaai-be/ai-engine/services/betting_brain.py` — büyük revizyon:
|
||||
- **HARD_MIN_SAMPLES = 50** floor (calibrator bypass <50 sample)
|
||||
- **`ev_edge < 0.0` HARD VETO** (`negative_ev_edge`)
|
||||
- **`ev_edge >= 0.20` HARD VETO** (`ev_edge_too_high_trap`)
|
||||
- **`MUTED_MARKETS = {"BTTS"}`** — backtest no profitable config bulduğu için
|
||||
- **`MARKET_OPTIMAL_FILTERS`** — MS ve OU25 için grid-search'ten gelen optimal envelope
|
||||
- **`_score_consistent_markets()`** — skor tahminine uymayan picks elimine
|
||||
- **`judge()` score coherence filter** — main_pick coherent set'ten seçilir
|
||||
- **HTFT reversal cross-check** — Man City 1/2 senaryosu
|
||||
|
||||
### C. AI engine — model & calibration
|
||||
| Dosya | Değişiklik |
|
||||
|---|---|
|
||||
| `ai-engine/models/calibration.py` | HARD_MIN_SAMPLES floor + sample-weighted blend formülü değişti |
|
||||
| `ai-engine/models/calibration/*.pkl` | **10 calibrator retrain** (ms_home/draw/away, ou15/25/35, btts, ht_home/draw/away) — 4989-5000 sample her biri |
|
||||
|
||||
### D. AI engine — orchestrator feature builder
|
||||
`ai-engine/services/orchestrator/feature_builder.py`:
|
||||
- Hardcoded `home_position=10, away_position=10` → real `data.home_position` kullanılıyor
|
||||
- Cup detection upper'a taşındı, `is_cup_match` UpsetEngine'e geçiyor
|
||||
- Total teams parametresi UpsetEngine'e geçiyor
|
||||
|
||||
`ai-engine/services/orchestrator/data_loader.py`:
|
||||
- `_estimate_league_position` artık **sezon filtresi** (son 300 gün) kullanıyor
|
||||
|
||||
### E. AI engine — scripts (yeni)
|
||||
| Dosya | Ne yapıyor |
|
||||
|---|---|
|
||||
| `ai-engine/scripts/diagnostic_backtest.py` | Per-bet diagnostic backtest (CSV+JSON+TXT output) |
|
||||
| `ai-engine/scripts/analyze_backtest_csv.py` | Backtest CSV üzerinde root-cause hipotez testleri |
|
||||
| `ai-engine/scripts/optimize_filters.py` | Grid search per-market optimal threshold |
|
||||
| `ai-engine/scripts/compare_backtests.py` | İki CSV karşılaştırması verdict ile |
|
||||
| `ai-engine/scripts/test_score_coherence.py` | Coherence filter smoke test (LAFC senaryosu) |
|
||||
|
||||
### F. Social poster modülü (NestJS)
|
||||
| Dosya | Değişiklik |
|
||||
|---|---|
|
||||
| `src/modules/social-poster/social-poster.service.ts` | Cron 15→10 dk, window 10-60, MAX_POSTS_PER_RUN, getHealthStatus() |
|
||||
| `src/modules/social-poster/image-renderer.service.ts` | SEO filename + metadata sidecar (.json) |
|
||||
| `src/modules/social-poster/caption-generator.service.ts` | SEO hashtag stratejisi (12 küratör tag) |
|
||||
| `src/modules/social-poster/social-poster.controller.ts` | `/health` public + `/preview-png/:matchId` + `/run-now` endpoints |
|
||||
| `mds/SOCIAL_POSTER_SETUP.md` **(YENİ)** | Env vars + API key alma adımları + test komutları |
|
||||
|
||||
### G. Modern image rendering (deneme)
|
||||
| Dosya | Açıklama |
|
||||
|---|---|
|
||||
| `src/scripts/render-social-card-v3.ts` | satori + resvg-js ile modern HTML→PNG rendering (Twemoji top + bayrak) |
|
||||
| `src/modules/social-poster/assets/*.svg` | Twemoji futbol/basket/bayrak SVG'leri |
|
||||
|
||||
### H. Yapılan DB değişiklikleri (idempotent — tekrar koşturulursa sorun yok)
|
||||
| İşlem | Etki |
|
||||
|---|---|
|
||||
| `football_ai_features` 4008+ satır backfill | Son 60 günün FT maçları için feature row var artık (calculator_ver=feature_enrichment_task_v1) |
|
||||
| Python enrichment koştu | h2h, referee, possession, league_avg, implied_* hepsi gerçek değerlerle dolu (181,614+ satır enriched) |
|
||||
| Calibrator dosyaları yazıldı | `ai-engine/models/calibration/*.pkl` overwritten |
|
||||
|
||||
---
|
||||
|
||||
## 📂 Önemli dosya konumları (OneDrive synced)
|
||||
|
||||
```
|
||||
iddaai-be/
|
||||
├── mds/
|
||||
│ ├── SESSION_HANDOFF.md ← BU DOSYA
|
||||
│ └── SOCIAL_POSTER_SETUP.md ← social poster env+keys
|
||||
├── ai-engine/
|
||||
│ ├── reports/ ← BACKTEST CIKTILARI
|
||||
│ │ ├── diagnostic_backtest_*.csv,json,txt
|
||||
│ │ └── filter_optimization_patch.json
|
||||
│ ├── validation_full.log ← validation backtest canlı log
|
||||
│ ├── diagnostic_backtest_run.log ← önceki backtest log
|
||||
│ ├── enrichment_run3.log ← enrichment koşma log
|
||||
│ └── calibration_run.log ← calibrator retrain log
|
||||
├── public/predictions/ ← render edilmiş social card PNG/JSON
|
||||
└── src/scripts/ ← tüm yeni script'ler
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Erişim bilgileri
|
||||
|
||||
### Pi sunucu (feeder + prod stack)
|
||||
- **SSH**: `haruncan@95.70.252.214:2222`
|
||||
- **Şifre**: `M594xH%$iM&4MM`
|
||||
- **Plink kullan**: `~/plink.exe -ssh -P 2222 -pw '<password>' -hostkey 'SHA256:iq0YVI/4J897sf9dkksI7QzetpLCD0l57ZMX4UissI8' haruncan@95.70.252.214`
|
||||
- **Docker**: `iddaai-be`, `iddaai-ai-engine`, `iddaai-fe`, `iddaai-postgres`, `iddaai-redis`, `gitea`
|
||||
|
||||
### DB (uzak Postgres @ Pi)
|
||||
- **SSH tunnel function**: `iddaai-db` PowerShell fonksiyonu (yerel makinedeki profile'da kayıtlı)
|
||||
- **Tunnel: localhost:5432 → Pi:5432**
|
||||
- **Connection string**: `postgresql://iddaai_user:IddaA1_S4crET!@localhost:5432/iddaai_db?schema=public`
|
||||
- **MCP**: Claude'un postgres MCP'si bu tunnel üzerinden çalışıyor (restricted mode, read-only)
|
||||
|
||||
---
|
||||
|
||||
## 📊 BACKTEST sonuçları geçmişi
|
||||
|
||||
### Backtest #1 — In-sample grid search (2026-05-11 → 05-24, 1000 maç)
|
||||
- **CSV**: `ai-engine/reports/diagnostic_backtest_20260525_035649.csv`
|
||||
- **TXT**: `ai-engine/reports/diagnostic_backtest_20260525_035649.txt`
|
||||
- **Toplam playable**: 524 bet
|
||||
- **Hit rate**: %54.77
|
||||
- **ROI**: **−%16.73** (baseline kötü)
|
||||
- **Grid-search'ten çıkan optimal filtreler (in-sample)**:
|
||||
- MS: edge [-5%, +15%], V27 AGREE zorunlu → +%8.23 (21 bet)
|
||||
- OU25: odds ≥ 1.80, edge ≤ +15% → +%28.91 (20 bet)
|
||||
- BTTS: tüm config'lerde kayıp → MUTE
|
||||
- **Aggregate optimize**: 95 bet, ROI +%2.16 (in-sample)
|
||||
|
||||
### Backtest #2 — Validation (2026-05-01 → 05-14, KOŞUYOR)
|
||||
- **Bitince konum**: `ai-engine/reports/diagnostic_backtest_<yeni_timestamp>.{csv,json,txt}`
|
||||
- **Karşılaştırma çalıştır**: `python scripts/compare_backtests.py` (otomatik en yeni 2'yi alır)
|
||||
- **Beklenen sonuç**: ROI ≥ 0 → out-of-sample doğrulama BAŞARILI; in-sample overfit değil
|
||||
|
||||
---
|
||||
|
||||
## ❓ Backtest BİTTİĞİNDE yapılacak (yeni session'da bu kısımdan başla)
|
||||
|
||||
### 1. Sonucu oku
|
||||
```powershell
|
||||
cd C:\Users\fahri\OneDrive\المستندات\GitHub\iddaai\iddaai-be\ai-engine
|
||||
Get-ChildItem reports\diagnostic_backtest_*.txt | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | Get-Content
|
||||
```
|
||||
|
||||
### 2. Karşılaştır
|
||||
```powershell
|
||||
python scripts\compare_backtests.py
|
||||
```
|
||||
|
||||
Bu otomatik en yeni 2 backtest'i karşılaştırır, **VERDICT** verir:
|
||||
- ✅ "FILTERS WORK" → ROI pozitif AND improved
|
||||
- 🟡 "PARTIAL" → improved ama hâlâ negatif
|
||||
- ❌ "OVERFITTING" → validation ROI collapse
|
||||
|
||||
### 3. Karara göre 2 yol
|
||||
|
||||
**Eğer ROI ≥ +%2 ve overfit yok:**
|
||||
- `/sc:design` ile UI/API contract → Sprint 1
|
||||
- Sprint 1: top-5 skor + evidence panel + "why" cümlesi
|
||||
- Test edip prod'a aç
|
||||
|
||||
**Eğer ROI negatif veya overfit:**
|
||||
- `analyze_backtest_csv.py` ile loss diagnostic
|
||||
- Hangi market hâlâ kötü → tighten filter veya mute
|
||||
- Calibrator recalibrate (özellikle BTTS dışındakiler için yeni sample)
|
||||
- Tekrar backtest
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Bilinen açık problemler / sorular
|
||||
|
||||
1. **Coherence filter validate edilmedi production-side** — smoke test 20/20 ama gerçek production data ile karşılaştırma yok
|
||||
2. **Lineup-overlap last-5 hesabı** — yazılmadı, requirements doc'ta F8 var
|
||||
3. **Skor top-5 distribution** — Poisson zaten hesaplıyor, surface edilmedi (UI tarafı)
|
||||
4. **"Why" cümlesi main_pick'te** — boş, doldurulması gerek
|
||||
5. **Cards/Corners/RED CARD model** — yok, "henüz desteklenmiyor" placeholder ile bırak (kullanıcı onayladı: mevcut market'ler sağlamlaşsın)
|
||||
6. **Orphan match_id 51 satır** — `prediction_runs` içinde, `matches`'ta yok. Sample noise, geçiştirilebilir.
|
||||
7. **opening_value feeder bug** — `odds_movement_*` SQL yazıyor ama tüm değerler 0 (opening == closing). Feeder upstream sorun. Düşük öncelik.
|
||||
|
||||
---
|
||||
|
||||
## 🚦 Yeni Claude session'ında ilk komut
|
||||
|
||||
```
|
||||
Bu projeye yeni bağlandım. Lütfen aşağıdaki dosyayı oku ve bana proje durumunu özet ver:
|
||||
|
||||
C:\Users\fahri\OneDrive\المستندات\GitHub\iddaai\iddaai-be\mds\SESSION_HANDOFF.md
|
||||
|
||||
Sonra validation backtest'in sonucuna bak:
|
||||
- C:\Users\fahri\OneDrive\المستندات\GitHub\iddaai\iddaai-be\ai-engine\reports\
|
||||
içindeki en yeni diagnostic_backtest_*.txt dosyasını oku
|
||||
- compare_backtests.py script'ini koş, verdict göster
|
||||
- Verdict'e göre sonraki adımı öner
|
||||
```
|
||||
|
||||
Buradan devam eder. Tüm context bu doc'ta + dosyalarda + DB'de.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Requirements spec (sıkıştırılmış)
|
||||
|
||||
**Ürün**: UI-tetikli per-match analiz, bahis uzmanı seviyesi
|
||||
**Trigger**: User tıklar, on-demand
|
||||
**Output**: main_pick + value_pick + tüm market olasılıkları + tek HT/FT skoru + top-5 skor dağılımı + evidence panel
|
||||
**Kapsam**: Mevcut market'ler sağlamlaştırılır, yeni market eklenmez (kullanıcı onayı)
|
||||
**Quality bar**: Calibration sapması ±2-5pp per market, NaN yok, response <3sn
|
||||
**Validation**: Out-of-sample backtest (1500 maç, May 1-14) — KOŞUYOR
|
||||
|
||||
---
|
||||
|
||||
**SON NOT**: Backtest'in TAMAMLANMASINI bekle (~22:00). Laptop'u kapatma. Bittiğinde OneDrive senkronize eder, başka makinede otomatik orada olur. Yeni session'da bu dosyayı oku, sonuçlara bak, devam et.
|
||||
@@ -0,0 +1,212 @@
|
||||
# Social Poster — Setup & Operations
|
||||
|
||||
Otomatik tahmin kartı üretip Twitter / Facebook / Instagram'a postlayan modül.
|
||||
Cron her **10 dakikada bir** çalışır, **yaklaşan 10-60 dk içindeki maçları**
|
||||
yakalar, AI Engine'den tahmin alır, 1080×1080 görsel üretir, caption üretir,
|
||||
3 platforma post eder.
|
||||
|
||||
## 1) ENV değişkenleri
|
||||
|
||||
`.env`'ye ekle:
|
||||
|
||||
```bash
|
||||
# Master switch — false ise cron çalışmaz, manual endpoint'ler de boş döner.
|
||||
SOCIAL_POSTER_ENABLED=true
|
||||
|
||||
# Hangi sporlar (virgüllü liste). Varsayılan: football,basketball
|
||||
SOCIAL_POSTER_SPORTS=football,basketball
|
||||
|
||||
# Yaklaşan maç penceresi (dakika). Varsayılan 10-60.
|
||||
SOCIAL_POSTER_WINDOW_MIN=10
|
||||
SOCIAL_POSTER_WINDOW_MAX=60
|
||||
|
||||
# Tek cron koşusunda kaç maç post edilir (rate-limit koruması). Varsayılan 5.
|
||||
SOCIAL_POSTER_MAX_PER_RUN=5
|
||||
|
||||
# Public base URL — Instagram media upload için fotoğrafın HTTPS'ten erişilebilir
|
||||
# olması ŞART. Localhost ile IG çalışmaz; production domain veya ngrok kullan.
|
||||
APP_BASE_URL=https://api.iddaai.com
|
||||
|
||||
# AI Engine URL (orchestrator)
|
||||
AI_ENGINE_URL=http://localhost:8000
|
||||
|
||||
# ─── Twitter / X ───
|
||||
TWITTER_API_KEY=...
|
||||
TWITTER_API_SECRET=...
|
||||
TWITTER_ACCESS_TOKEN=...
|
||||
TWITTER_ACCESS_SECRET=...
|
||||
|
||||
# ─── Meta (Facebook + Instagram) ───
|
||||
META_PAGE_ACCESS_TOKEN=... # FB Page'in long-lived access token'ı
|
||||
META_PAGE_ID=... # FB Page numeric ID
|
||||
META_IG_USER_ID=... # IG Business account numeric ID
|
||||
META_GRAPH_API_VERSION=v25.0 # opsiyonel
|
||||
|
||||
# ─── Caption AI (opsiyonel — yoksa template caption kullanılır) ───
|
||||
ENABLE_GEMINI=true
|
||||
GEMINI_API_KEY=...
|
||||
GEMINI_MODEL=gemini-1.5-flash
|
||||
|
||||
# Veya local Ollama:
|
||||
OLLAMA_BASE_URL=http://localhost:11434
|
||||
SOCIAL_POSTER_OLLAMA_MODEL=llama3.1
|
||||
```
|
||||
|
||||
## 2) API anahtarlarını alma
|
||||
|
||||
### Twitter / X
|
||||
1. https://developer.x.com → Project + App oluştur
|
||||
2. App'ın "Keys and tokens" → "API Key", "API Secret" al
|
||||
3. User authentication settings → "Read and write and Direct Message"
|
||||
4. "Access Token and Secret" generate et (bu hesap adına post eder)
|
||||
5. **Free tier**: 1500 tweet/ay, 50 post/24 saat — 10 dk'lık cron'la günde
|
||||
~144 koşu × 5 post = 720 potansiyel post → free tier yetmez, **Basic plan
|
||||
($200/ay)** lazım. Cron interval'i 30 dk'ya alıp 50/gün kalmak istersen
|
||||
`@Cron("*/30 * * * *")` olarak değiştir.
|
||||
|
||||
### Meta (Facebook + Instagram)
|
||||
1. https://developers.facebook.com → App oluştur (type: Business)
|
||||
2. Facebook Page bağla (mevcut sayfan yoksa oluştur)
|
||||
3. Instagram Business hesabını Facebook Page'e bağla
|
||||
4. Graph API Explorer'dan **page access token** al (User token değil!)
|
||||
5. Long-lived token'a çevir (60 gün geçerli, refresh edilebilir)
|
||||
6. **Page ID**: `https://graph.facebook.com/me/accounts?access_token=...`
|
||||
7. **IG User ID**: `graph.facebook.com/{pageId}?fields=instagram_business_account&access_token=...`
|
||||
8. Required permissions: `pages_show_list`, `pages_manage_posts`,
|
||||
`pages_read_engagement`, `instagram_basic`, `instagram_content_publish`
|
||||
|
||||
### Gemini (caption AI — opsiyonel)
|
||||
- https://aistudio.google.com → API key (free tier yeterli, günde ~1500 istek)
|
||||
- `ENABLE_GEMINI=true` + `GEMINI_API_KEY=...`
|
||||
- Gemini yoksa template caption kullanılır (yine SEO'lu, sadece daha statik)
|
||||
|
||||
## 3) Test komutları
|
||||
|
||||
```bash
|
||||
# Servisi başlat
|
||||
npm run start:dev
|
||||
|
||||
# Health endpoint — auth gerekmez
|
||||
curl http://localhost:3005/social-poster/health | jq
|
||||
|
||||
# Manuel preview (görsel + JSON) — superadmin token gerekir
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3005/social-poster/preview/<matchId>
|
||||
|
||||
# Görseli tarayıcıda direkt göster
|
||||
open http://localhost:3005/social-poster/preview-png/<matchId>?token=$TOKEN
|
||||
|
||||
# Manuel post (tek maç, tüm platformlara) — superadmin token
|
||||
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3005/social-poster/post/<matchId>
|
||||
|
||||
# Cron'u beklemeden full sweep koş — superadmin token
|
||||
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3005/social-poster/run-now
|
||||
```
|
||||
|
||||
## 4) SEO özellikleri
|
||||
|
||||
### Image dosya adı (SEO)
|
||||
Eskiden: `prediction_basketball_xyz12345_1716595200000.jpg` (opaque)
|
||||
Yeni: `sampiyonlar-ligi-unicaja-malaga-vs-aek-20260525.jpg` (Google indexable)
|
||||
|
||||
### Yan dosya: metadata sidecar
|
||||
Her görsel için aynı dizinde `.json`:
|
||||
- `title`, `description`, `og:*`, `schema.org SportsEvent`, `picks[]`
|
||||
- Sayfada `<head>` Open Graph + Twitter Cards bu dosyadan beslenir
|
||||
- Schema.org markup zengin sonuç (Google rich snippet) sağlar
|
||||
|
||||
### Caption (SEO + hashtags)
|
||||
Her post 12'ye kadar küratör hashtag içerir:
|
||||
- Marka: `#MaçTahmini #İddaa #BugünMaç`
|
||||
- Spor: `#Futbol #Basketbol #FutbolTahmin`
|
||||
- Lig: `#SüperLig #PremierLeague #ŞampiyonlarLigi #EuroLeague #NBA`
|
||||
- Bölge: `#Türkiye #İngiltere #İspanya`
|
||||
- Takım: `#Galatasaray #Fenerbahçe`
|
||||
- Gün: `#PazarTahmini #CumartesiTahmini`
|
||||
- Market: `#AltÜst #KGVar #ÇifteŞans #MaçSonucu #Handikap`
|
||||
|
||||
LLM (Gemini) caption üretiyorsa hashtag'leri çıkarır; sistem kendi
|
||||
hashtag set'ini ekler. Tutarlı index için tek kaynak.
|
||||
|
||||
## 5) İzleme
|
||||
|
||||
```bash
|
||||
# Health endpoint — periyodik monitor
|
||||
curl http://localhost:3005/social-poster/health | jq
|
||||
|
||||
# Sample output:
|
||||
{
|
||||
"enabled": true,
|
||||
"sports": ["football", "basketball"],
|
||||
"window_min_minutes": 10,
|
||||
"window_max_minutes": 60,
|
||||
"max_posts_per_run": 5,
|
||||
"top_leagues_loaded": 42,
|
||||
"posted_match_count": 137,
|
||||
"last_run_at": "2026-05-25T03:10:00.123Z",
|
||||
"last_run_result": { "posted": 4, "skipped": 1, "errors": 0 },
|
||||
"twitter_available": true,
|
||||
"meta_facebook_available": true,
|
||||
"meta_instagram_available": true,
|
||||
"ai_engine_url": "http://localhost:8000",
|
||||
"app_base_url": "https://api.iddaai.com"
|
||||
}
|
||||
```
|
||||
|
||||
`posted_match_count` `storage/social-poster-posted.json`'dan okunur, son 500
|
||||
match ID hafızada — aynı maçı 2 kere post etmez.
|
||||
|
||||
## 6) Rate limit ipuçları
|
||||
|
||||
| Platform | Free limit | Tedbir |
|
||||
|---|---|---|
|
||||
| Twitter | 50 post/24 saat | `SOCIAL_POSTER_MAX_PER_RUN=2` + cron `*/30` → günde ~96 |
|
||||
| Facebook | ~200 post/saat (Page) | Default config rahat |
|
||||
| Instagram | 25 post/24 saat | `MAX_PER_RUN=1` + cron `*/60` → günde 24, sınırın hemen altında |
|
||||
|
||||
IG en sıkı sınır — production için **IG ayrı cron'da daha seyrek post**
|
||||
yapılması önerilir (kod henüz tek cron, ileride ayrılabilir).
|
||||
|
||||
## 7) Hangi maçlar seçilir?
|
||||
|
||||
`top_leagues.json` dosyasındaki league_id'ler içinden:
|
||||
- Şu anda 10-60 dakika sonra başlayacak
|
||||
- Daha önce post edilmemiş
|
||||
- `sport: football, basketball` filtresi geçen
|
||||
|
||||
`top_leagues.json` yoksa **tüm liglerden** maç seçer (hacmi yüksek tutar).
|
||||
Sadece premium ligler postlamak istersen dosyayı doldur.
|
||||
|
||||
## 8) Görsel formatı
|
||||
|
||||
- **Boyut**: 1080×1080 (Instagram square — Twitter da kabul ediyor)
|
||||
- **Format**: JPEG, quality 94
|
||||
- **Tema**: Sport'a göre değişir — football yeşil, basketball turuncu
|
||||
- **İçerik**: Lig logosu + ülke bayrağı, takım logoları + adları, HT skor,
|
||||
FT skor, top 3 tahmin (confidence ile), risk badge
|
||||
|
||||
Card layout `image-renderer.service.ts` içinde — value-pick yıldız ile
|
||||
işaretli, scenario top 3 listelenir, footer alt'ta tarih + brand.
|
||||
|
||||
## 9) Sık sorular
|
||||
|
||||
**Q: Görseller nereye yazılıyor?**
|
||||
`public/predictions/` (gitignored). ServeStatic ile `/predictions/<file>.jpg`
|
||||
URL'inden erişilir.
|
||||
|
||||
**Q: Eski görseller temizleniyor mu?**
|
||||
Hayır — manuel temizlik gerekir. Cron eklemek istersen `LimitResetterTask`
|
||||
örneği var.
|
||||
|
||||
**Q: AI Engine çalışmıyorsa ne olur?**
|
||||
Cron tahmin alamaz, log'a hata düşer, devam eder. Sonraki koşuda dener.
|
||||
|
||||
**Q: Bir maç 2 kere post ediliyor mu?**
|
||||
Hayır — `postedMatchIds` set'i Match ID bazında dedup yapar, dosyaya yazılır
|
||||
(restart-safe).
|
||||
|
||||
**Q: Caption Gemini olmadan ne kadar iyi?**
|
||||
Template caption tüm bilgileri + 12 hashtag içerir. SEO açısından yeterli,
|
||||
sadece anlatım daha statik. Gemini ile her post için özgün metin.
|
||||
Reference in New Issue
Block a user