Files
iddaai-be/mds/SOCIAL_POSTER_SETUP.md
T
fahricansecer 988ee2f50d 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>
2026-05-25 20:43:28 +03:00

213 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.