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

7.7 KiB
Raw Blame History

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:

# 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ı

# 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

# 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.