# Social Poster Modülü — Otomatik Sosyal Medya Paylaşım Sistemi

Son güncelleme: 5 Mayıs 2026

---

## 1. Amaç

Top liglerdeki maçların AI tahminlerini **otomatik olarak görselleştirip** Instagram, Facebook ve X (Twitter) üzerinden paylaşmak. Her maç için 30 dakika önceden tahmin alınıp, 1080×1920 (9:16 Instagram Story) formatında poster üretilir.

---

## 2. Mimari Akış

```
Cron (*/15 dk) → LiveMatch sorgusu (top_leagues.json filtresi)
  → AI Engine V20+ POST /v20plus/analyze/{match_id}
    → PredictionCardDto oluştur
      → Node Canvas ile futbol/basketbol 1080x1080 JPEG render
        → Ollama/Gemini ile Türkçe SEO uyumlu caption üret
          → Twitter / Facebook / Instagram API'ye paylaş
```

---

## 3. Dosya Yapısı

```
src/modules/social-poster/
├── social-poster.module.ts       # NestJS modül tanımı
├── social-poster.controller.ts   # Test endpointleri (preview, post)
├── social-poster.service.ts      # Ana orkestrasyon servisi
├── image-renderer.service.ts     # Node Canvas ile görsel üretimi
├── caption-generator.service.ts  # Gemini ile post metni üretimi
├── twitter.service.ts            # Twitter/X API entegrasyonu
├── meta.service.ts               # Facebook + Instagram Graph API
└── dto/
    └── prediction-card.dto.ts    # PredictionCardDto, TopPick, SocialPostResult
```

---

## 4. Temel Servisler

### 4.1 SocialPosterService

**Cron:** Her 15 dakikada bir çalışır. Varsayılan olarak 25–45 dakika içinde başlayacak futbol ve basketbol maçlarını `top_leagues.json` filtresiyle bulur.

**Tekrar paylaşım koruması:** Başarılı platform paylaşımı alan maç ID'leri `storage/social-poster-posted.json` içinde son 500 kayıt olarak tutulur. Servis restart sonrası aynı maç tekrar paylaşılmaz.

**Pipeline:** `predictAndPost(match)` → Tahmin al → Görsel üret → Caption üret → Paylaş

**AI Engine İsteği:**

```typescript
// POST — GET değil! AI Engine v20plus POST bekler.
axios.post(`${aiEngineUrl}/v20plus/analyze/${matchId}`, null, {
  timeout: 30000,
});
```

**Veri Haritalandırma (V20+ → CardDto):**

| V20+ Response Alanı     | CardDto Alanı                                  |
| ----------------------- | ---------------------------------------------- |
| `score_prediction.ht`   | `htScore` (ör: "1-1")                          |
| `score_prediction.ft`   | `ftScore` (ör: "2-1")                          |
| `main_pick.confidence`  | `scoreConfidence` (ör: 65)                     |
| `bet_summary[]` (array) | `topPicks[]` (ilk 3, confidence'a göre sıralı) |
| `risk.level`            | `riskLevel` (LOW/MEDIUM/HIGH/EXTREME)          |
| `match_info.home_team`  | `homeTeam` (fallback)                          |

**Bet Summary Market Kodları:**

| Kod     | Türkçe          | English           |
| ------- | --------------- | ----------------- |
| MS      | Maç Sonucu      | Match Result      |
| OU15    | Üst 1.5 Gol     | Over 1.5          |
| OU25    | Üst 2.5 Gol     | Over 2.5          |
| OU35    | Üst 3.5 Gol     | Over 3.5          |
| BTTS    | Karşılıklı Gol  | Both Teams Score  |
| DC      | Çifte Şans      | Double Chance     |
| HT      | İlk Yarı Sonucu | Half Time Result  |
| HT_OU05 | İY 0.5 Üst/Alt  | HT Over/Under 0.5 |
| OE      | Tek/Çift        | Odd/Even          |
| HTFT    | İY/MS           | HT/FT             |

### 4.2 ImageRendererService

**Motor:** `node-canvas` (Puppeteer/HTML yok — sunucu performansı için)

**Çıktı:** `public/predictions/prediction_{matchId}_{timestamp}.png`

**Boyut:** 1080×1920 px (Instagram Story / Reels uyumlu)

**Özellikler:**

- Koyu gradient arka plan (#0a0e27 → #1a1040 → #0d1b2a)
- Lig adı + tarih başlık satırı
- Takım logoları (200×200px) — `public/uploads/teams/` altından okunur
- İlk Yarı / Maç Sonu skor kutuları
- Güven yüzdesi badge'i
- En iyi 3 tahmin (progress bar + yüzde)
- Risk seviyesi badge'i (renk kodlu)
- `iddaai.com` filigran (saydam, tekrarlı, döndürülmüş)
- Alt bilgi: "⚡ AI Powered by SuggestBet"

**Logo Çözümleme:**

```
1. Yerel dosya varsa → public/uploads/teams/xxx.png oku
2. URL http ile başlıyorsa → HTTP ile indir
3. Bulunamazsa → logo olmadan devam et (graceful fallback)
```

### 4.3 CaptionGeneratorService

Gemini API kullanarak maç verisi JSON'ından Türkçe post metni üretir.

### 4.4 TwitterService & MetaService

Üretilen görsel + caption'ı ilgili platformlara yükler.

---

## 5. API Endpointleri

| Method | Path                                  | Auth    | Açıklama                                             |
| ------ | ------------------------------------- | ------- | ---------------------------------------------------- |
| GET    | `/api/social-poster/preview/:matchId` | @Public | Sadece görsel üret + caption üret (paylaşma)         |
| POST   | `/api/social-poster/post/:matchId`    | @Public | Görsel üret + caption üret + tüm platformlara paylaş |

> **Not:** Test endpointleri `@Public()` dekoratörüyle auth bypass edilmiştir. Production'da kaldırılmalı veya admin-only yapılmalıdır.

---

## 6. Environment Değişkenleri

| Key                                           | Zorunlu | Varsayılan               | Açıklama                                                             |
| --------------------------------------------- | ------- | ------------------------ | -------------------------------------------------------------------- |
| `AI_ENGINE_URL`                               | ✅      | `http://localhost:8000`  | AI Engine base URL                                                   |
| `APP_BASE_URL`                                | ✅      | `http://localhost:3000`  | Meta'nın çekebileceği public görsel URL'i ve logo URL çözümleme için |
| `SOCIAL_POSTER_ENABLED`                       | ❌      | `false`                  | Cron job'ı aktif/pasif                                               |
| `SOCIAL_POSTER_SPORTS`                        | ❌      | `football,basketball`    | Otomatik paylaşılacak sporlar                                        |
| `SOCIAL_POSTER_WINDOW_MIN`                    | ❌      | `25`                     | Başlama zaman penceresi alt sınırı (dakika)                          |
| `SOCIAL_POSTER_WINDOW_MAX`                    | ❌      | `45`                     | Başlama zaman penceresi üst sınırı (dakika)                          |
| `OLLAMA_BASE_URL`                             | ❌      | `http://localhost:11434` | Lokal LLM endpoint'i                                                 |
| `OLLAMA_MODEL` / `SOCIAL_POSTER_OLLAMA_MODEL` | ❌      | —                        | Caption üretiminde kullanılacak lokal model                          |
| `GOOGLE_API_KEY`                              | ❌      | —                        | Gemini caption için                                                  |
| Twitter API keys                              | ❌      | —                        | X medya upload + `/2/tweets` paylaşımı için OAuth 1.0a user context  |
| `META_GRAPH_API_VERSION`                      | ❌      | `v25.0`                  | Meta Graph API sürümü                                                |
| Meta API keys                                 | ❌      | —                        | FB/IG paylaşım için                                                  |

---

## 7. Bağımlılıklar

```json
{
  "canvas": "^2.x", // Node Canvas — görsel üretimi
  "axios": "^1.x", // HTTP istekleri (AI Engine + logo indirme)
  "@nestjs/schedule": "*" // Cron job desteği
}
```

> **Kaldırılan:** `puppeteer` — performans ve Raspberry Pi uyumluluğu için `canvas` ile değiştirildi.

---

## 8. Deploy Notları

### Raspberry Pi (ARM64)

```bash
# canvas native bağımlılıkları (Dockerfile'da)
RUN apk add --no-cache cairo-dev pango-dev jpeg-dev giflib-dev librsvg-dev
```

### Port Yönetimi

| Servis         | Port                                        |
| -------------- | ------------------------------------------- |
| NestJS Backend | 3000 (production: 150X)                     |
| AI Engine      | 8000 (dev: 8005 — Windows port kısıtlaması) |

### Dosya Sistemi

```
public/
├── uploads/teams/         # Takım logoları (PNG)
└── predictions/           # Üretilen poster görselleri (PNG)
```

---

## 9. Bilinen Sorunlar & Çözümler

| Sorun                                   | Sebep                                | Çözüm                                     |
| --------------------------------------- | ------------------------------------ | ----------------------------------------- |
| `WinError 10013` port erişim hatası     | Windows Hyper-V port rezervasyonu    | Farklı port kullan (8005)                 |
| `Invalid prisma.liveMatch.findUnique()` | Prisma client eskimiş                | `npx prisma generate` çalıştır            |
| `405 Method Not Allowed` AI Engine      | GET yerine POST gerekiyor            | `axios.post()` kullan                     |
| Logolar görünmüyor (lokal dev)          | Logo dosyaları sunucuda, lokalde yok | Deploy'da çalışır, lokal'de graceful skip |
