# 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/ # Görseli tarayıcıda direkt göster open http://localhost:3005/social-poster/preview-png/?token=$TOKEN # Manuel post (tek maç, tüm platformlara) — superadmin token curl -X POST -H "Authorization: Bearer $TOKEN" \ http://localhost:3005/social-poster/post/ # 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 `` 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/.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.