# Proje Durumu — Futbol Otomasyon Botu

> Son güncelleme: **17.08.2026** · Bu belge projenin tam hâlini, alınan kararları,
> yaşanan sorunları ve nerede kaldığımızı anlatır. Yeni bir oturuma başlarken
> önce bunu oku.

---

## 1. Proje nedir

X (Twitter) `@forkspor` hesabında **7/24 çalışan otomatik futbol içerik botu**.
Gerçek bir spor ajansı gibi davranır: canlı maç anlatımı, haber paylaşımı,
resmi yayıncının gol videolarını paylaşma, günlük fikstür/sonuç özetleri.
Tamamı web panelinden yönetilir.

**Kapsam:** Süper Lig, Trendyol 1. Lig, Şampiyonlar/Avrupa/Konferans Ligi,
Premier Lig, La Liga, Serie A, Bundesliga, Ligue 1.

---

## 2. Şu anki durum (17.08.2026)

### Çalışır durumda
| Bileşen | Durum |
|---|---|
| Panel | http://127.0.0.1:8000 — 16 sayfa, hepsi test edildi |
| X hesabı | `@forkspor` — doğrulandı, `durum=ok` |
| DeepSeek | Anahtar girili (şifreli), `deepseek-v4-flash` |
| Paylaşım | **AÇIK** (`posting_enabled=true`, mod `auto`) |
| Haber akışı | **AÇIK** — 6 RSS kaynağı |
| Kaynak hesap | `@beINSPORTS_TR` izleniyor, **otomatik alıntı KAPALI** (elle onay) |
| Bütçe planlayıcı | **AÇIK** |
| Veri | 399 maç, 136 haber, 20 kaynak gönderisi |

### Gerçekten atılmış tweetler (6 adet)
| Zaman | Tür | ID |
|---|---|---|
| 16.08 23:14 | haber | 2089128707067727952 |
| 16.08 23:20 | transfer | 2089130153829863761 |
| 16.08 23:22 | haber | 2089130777892069473 |
| 17.08 00:24 | haber | 2089146238285271519 |
| 17.08 00:30 | haber | 2089147797031334009 |
| 17.08 00:54 | kaynak videosu | 2089153970279202952 |

Ayrıca **silinen 3 tweet** (eski biçim, kullanıcı isteğiyle): 2 alıntı kartlı,
1 fotoğrafa düz link.

### Ayarlar
```
posting_enabled = true          automation_mode = auto
daily_post_cap = 220            budget_enabled = true
min/max_gap_seconds = 90/400    (ana akış  ≈ 14 tweet/saat)
reply_min/max_gap = 15/45       (thread    ≈ 95 tweet/saat)
active_hours = 8–24             news_per_hour = 2
quotes_per_hour = 12            content_engine = deepseek
```

---

## 3. Mimari

```
app/
├── main.py                  FastAPI + 16 panel sayfası
├── core/
│   ├── security.py          Fernet şifreleme (API anahtarı, cookie'ler)
│   └── timeutil.py          Türkiye günü sınırları (UTC+3)
├── db/
│   ├── models.py            9 tablo
│   ├── crud.py              Ayarlar, hesaplar, ligler, loglar
│   ├── database.py          SQLite motoru (WAL + busy_timeout)
│   └── migrate.py           Hafif ALTER TABLE göçü
├── data_engine/
│   ├── sofascore.py         Maç verisi (curl_cffi + Chrome taklidi)
│   └── sync.py              Fikstür + canlı olay tespiti
├── content_engine/
│   ├── generator.py         Maç olayı → tweet (DeepSeek + şablon)
│   ├── templates.py         143 şablon / 26 olay tipi
│   ├── quote_writer.py      Kaynak gönderisi → kendi yorumumuz
│   └── textutil.py          Satır düzeni, etiket temizliği, kısaltma
├── news_engine/
│   ├── rss.py               6 Türk spor kaynağı
│   ├── article.py           Tam makale metni (trafilatura)
│   ├── curator.py           Ön eleme + mükerrer ayıklama
│   └── writer.py            Haber → tweet (DeepSeek kalite kapısı)
├── x_engine/
│   ├── client.py            Tweet atma, doğrulama, rotasyon, medya yükleme
│   ├── twikit_patch.py      twikit'in kırık transaction-id üretimi yaması
│   ├── sources.py           İzlenen hesap tarama, sınıflama, maç eşleştirme
│   └── importance.py        Önem puanı (sıralama için)
├── scheduler/
│   ├── manager.py           11 zamanlanmış iş
│   ├── enqueue.py           Olay → kuyruk (kapsam filtresi + thread)
│   ├── dispatcher.py        Gönderim (ritim, thread zinciri, rotasyon)
│   ├── coverage.py          Anlatım yoğunluğu kademeleri
│   ├── budget.py            Günlük bütçe planlayıcı
│   ├── daily.py             Sabah fikstür / akşam sonuç / puan durumu
│   └── maintenance.py       Hesap sağlığı + eski kayıt temizliği
└── web/templates/           15 HTML sayfası
```

### Zamanlanmış işler
| İş | Sıklık | Görev |
|---|---|---|
| `live` | 25 sn | Canlı olay tarama → kuyruk |
| `dispatch` | 15 sn | Kuyruktan gönderim |
| `sources` | 3 dk | İzlenen hesapları tara, alıntı hazırla |
| `lineups` | 5 dk | İlk 11 + maç öncesi tanıtım |
| `news` | 20 dk | RSS tara → haber tweeti |
| `budget` | 20 dk | Kapsam planını güncelle |
| `fixtures` | 30 dk | Fikstür senkronu |
| `health` | 6 saat | X oturumu geçerli mi |
| `daily_morning` | 10:00 | Günün maç programı |
| `daily_evening` | 23:30 | Günün sonuç özeti |
| `standings` | Pzt 12:00 | Puan durumu |
| `cleanup` | 04:30 | Eski kayıtları buda |

---

## 4. Kritik kararlar ve NEDENLERİ

Bu bölüm en önemlisi — "neden böyle yapılmış?" sorusunun cevabı.

### 4.1 X erişimi: twikit (cookie), resmi API değil
Ücretli API istenmedi. `auth_token` + `ct0` cookie'leriyle giriş.
**Risk kabul edildi:** X şartlarına aykırı, hesap askıya alınabilir. Sistem
askıyı tespit edip hesabı rotasyondan düşürür ama riski sıfırlamaz.

### 4.2 Eş zamanlı maç sorunu — üç katmanlı çözüm
Cumartesi 19:00'da 15 maç birden başlıyor. İnsansı tempoda saatte ~15 tweet
atılabilir; 15 maçta ilk yarıda 30+ olay çıkar → goller "bayat" diye
**sessizce iptal oluyordu**.

1. **Kademe** (`coverage.py`) — her lig için anlatım yoğunluğu:
   `full` / `standard` / `minimal` / `result_only`. `auto` ise lig
   önceliğinden türetilir. Panelden lig lig ayarlanır.
2. **Thread** — bir maçın tüm olayları o maçın thread'ine yanıt olarak gider.
   Ana akış maç başına tek tweet görür.
   `Match.thread_tweet_id` (kök) + `Match.last_tweet_id` (zincir ucu).
3. **Tempo ayrımı** — thread yanıtları ana akışı doldurmadığı için 15–45 sn
   ile gidebilir; ana akış 90–400 sn insansı aralığında kalır.

### 4.3 Bütçe: tavan sabit, KAPSAM esner
Kullanıcı "otomatik artsın mı?" diye sordu. **Cevap: hayır.**
Günlük tavan bir güvenlik freni; otomatik artan tavan, en çok maçın olduğu
günde (hesabın zaten en görünür olduğu gün) kendini en yükseğe çıkarır —
frenin amacının tam tersi.

Bunun yerine: sistem günün fikstürüne bakıp kaç tweet gerekeceğini tahmin
eder, bütçeye sığmıyorsa **en az öncelikli** liglerden başlayarak **birer
adım** hafifletir ve sığar sığmaz durur. Kullanıcının seçtiği kademe
**tavandır** — planlayıcı yalnızca aşağı çeker.

> İlk sürüm aynı ligi dibe vurana kadar düşürüp sonrakine geçiyordu; tavan
> 120'de tek hamlede 8 ligi "sadece sonuç"a indirdi. Adım adım ve sırayla
> inecek şekilde düzeltildi.

### 4.4 Yayıncı içeriği — telif sınırı
`@beINSPORTS_TR` resmi yayıncı. Videolarını **indirip kendi hesabımıza
yüklemek telif ihlali** — yapılmaz.

**Denenen ve elenen biçimler (canlı test edildi):**
| Biçim | Sonuç |
|---|---|
| `attachment_url` (alıntı kartı) | Çalışıyor ama **kart oluşturuyor** — kullanıcı istemedi, tweetimiz kaynağın tekrarı gibi duruyor |
| Düz status linki metinde | **X reddediyor** (kod 344 AuthorizationError) |
| `/video/1` linki metinde | ✅ Kabul, `is_quote_status=False`, **kart yok** |

**Nihai biçim:**
- **Video** → kendi metnimiz + sonunda `{status_url}/video/1`
- **Fotoğraf** → link YOK; görsel `pbs.twimg.com`'dan indirilip
  `upload_media` ile **kendi tweetimize eklenir** (kullanıcının bilinçli
  kararı; skor kartı görselleri için kabul edildi — **video re-upload hâlâ
  yasak**)
- **Medyasız** → paylaşılmaz
- Kaynağın **metni kopyalanmaz** (DeepSeek kendi cümleleriyle yazar)
- **`#beINSPORTS` gibi marka etiketleri sert filtreyle silinir**

### 4.5 İçerik filtreleri (kaynak hesap)
Sınıflar ve davranış:
| Sınıf | Otomatik? | Örnek |
|---|---|---|
| `goal` | ✅ | "Dia Saba golü buluyor!" |
| `action` | ✅ | kurtarış, kırmızı kart, penaltı |
| `summary` | elle onay | "Maç Sonucu: ... 3-0" |
| `interview` | elle onay | 🎙️ basın toplantısı |
| `studio` | elle onay | 💬 yorumcu değerlendirmesi (konuşan kafa) |
| `promo` | ❌ **baştan elenir** | "🔗 İzlemek için:", "📲 ÖZET BURADA" |

`promo` kontrolü **en önce** yapılır, çünkü bu gönderilerde de "gol"
geçiyor ("⚽️ İŞTE GALİBİYET GOLÜ ... 🔗 Detaylar için:").

### 4.6 Önem puanı — ELEME DEĞİL SIRALAMA
Kullanıcı haklı olarak sordu: "beIN 20 gol paylaşıyorsa o 20'si de işimize
yaramaz mı?" Evet. Saatlik 3 tavanı fazla temkinliydi → **12** yapıldı.

Puanın işi zayıfı elemek değil, aynı anda 5 gol düştüğünde **sırayı**
belirlemek:
- **Durağan** (DB'de): tür + medya + lig önceliği + takım büyüklüğü
  (derbi +22) + anlatı gücü + **kaynağın RT/beğeni sayısı**
- **Dinamik** (seçim anında): tazelik (10 dk içinde +20)

Etkileşim verisi her taramada **bekleyen adaylar için tazelenir** — beIN
gönderisi ilk dakikalarda 0 RT ile geliyor, önemi sonradan belli oluyor.

### 4.7 Maç etiketi (#BJKvEYP) — öğrenilir, tahmin edilmez
Yayıncı her maç için etiket üretiyor; kullanınca o maçın konuşmasına dahil
oluyoruz. **Tahmin EDİLEMEZ** — kodlar kurala uymuyor (Beşiktaş→BJK,
Eyüpspor→EYP, Amed→AMD). Uydurulan etiket hiçbir konuşmaya bağlanmaz.
Yalnızca yayıncının gönderilerinden öğrenilir → `Match.hashtag`.

Ters yönde de kullanılır: takım adı geçmeyen kaynak tweetlerini maça
bağlamak için `#XXXvYYY` çözülür (alt dizi eşleşmesi + Türkçe normalize;
gevşek olduğu için **iki kodun da** aynı maçın iki takımına uyması şartı).
Gerçek veride 20 gönderinin 15'i doğru maça bağlandı.

### 4.8 Haberler
- **Tam metin** çekilir (trafilatura). RSS özetleri boş/tek cümle olduğu için
  "Italiano'dan Vlahovic sözleri!" gibi hiçbir şey anlatmayan tweetler
  çıkıyordu.
- Tam metin **yalnızca tweet'lenecek habere** çekilir (yüzlerce gereksiz
  indirme olmasın).
- **Pencere 30 dakika** — ajans 6 saatlik gelişmeyi "haber" diye atmaz.
- Anahtar kelime = ucuz **ön eleme**; asıl kalite kararı **DeepSeek'te**.
  Sebep: anahtar kelime listesi futbol bilgisini kodlayamıyor — "Arsenal 3-0
  Manchester City" cümlesinde tek bir futbol kelimesi yok.
- LLM kapısında **varsayılan PAYLAŞMAK**. İlk sürüm "şunları paylaşma"
  listesiyle yazılınca 10 haberin 9'unu eledi (gerçek transferler dahil).

### 4.9 Tweet biçimi — satır düzeni
Her üretici çıktıyı `" ".join(text.split())` ile düzleştiriyordu; bu LLM'in
koyduğu satır sonlarını da eziyor, tweetler tek blok duvar metin çıkıyordu.

`textutil.normalize()` satır yapısını **korur**, yalnızca fazlalığı temizler.
Etiketler **boş satırdan sonra** eklenir:
```
Dia Saba 23'te sahneye çıktı, farkı üçe çıkardı! ⚽
Amed 3 - 0 Erzurumspor

#AMDvERZ #SüperLig
```

---

## 5. Yaşanan sorunlar ve çözümleri

Bunlar tekrar karşılaşılabilecek gerçek arızalar.

### 5.1 twikit kutudan çıktığı hâliyle ÇALIŞMIYOR — 3 ayrı kırık nokta

**(a) `Couldn't get KEY_BYTE indices` — her isteği öldürüyordu**
twikit, `x-client-transaction-id` başlığı için `ondemand.s.<hash>a.js`
dosyasını arıyor, hash'i `"ondemand.s":"<hash>"` deseniyle buluyordu.
X paket formatını değiştirdi; hash artık iki ayrı haritada:
`59924:"ondemand.s"` (isim) ve `59924:"25d3ef4"` (hash).
→ Yama: `app/x_engine/twikit_patch.py`. **twikit güncellenirse yeniden doğrula.**

**(b) `client.user()` ölü**
X'in kaldırdığı v1.1 `account/settings` ucuna gidiyor (404). Ayrıca
`get_user_by_screen_name` / `search_user` / `user_id` hepsi
`KeyError: 'urls'` veriyor.
→ Doğrulama için **`get_latest_timeline(count=1)`** kullanılır (GraphQL).

**(c) `client.create_tweet()` MÜKERRER TWEET üretiyordu**
Kod sırası: `gql.create_tweet(...)` [tweet burada gider] →
`tweet_from_data(...)` [`KeyError: 'urls'` burada patlar].
Yani tweet gidiyor ama bize hata dönüyor; üst katman "başarısız" sayıp
tekrar gönderiyordu.
→ Alt seviye `client.gql.create_tweet()` doğrudan çağrılır, id ham yanıttan
(`rest_id`) okunur.

> **Genel ders:** twikit'te bir çağrı hata verdiğinde "işlem yapılmadı"
> varsayma. Yan etki hatadan ÖNCE gerçekleşmiş olabilir. Önce profili
> kontrol et, sonra DB'yi gerçekle eşitle.

### 5.2 SofaScore 403
Normal HTTP istemcileri TLS parmak izinden engelleniyor.
→ **`curl_cffi` + `impersonate="chrome"` şart.** FotMob da aynı şekilde engelli.
→ `/sport/football/scheduled-events/{tarih}` **404** — tarih bazlı çekme yok,
turnuva-sezon üzerinden gidilir.

### 5.3 DeepSeek reasoning token taşması
V4 Flash içsel "düşünme" yapıyor ve bu token'lar `max_tokens`'a sayılıyor
(ölçülen tipik ~70 ama 800'ü aşan vakalar loglandı). Dar sınırda metin
cümlenin ortasında kesiliyordu; güvenlik kontrolü yarım tweet yayınlamak
yerine atlıyor, iyi içerikler boşa "elendi" görünüyordu.
→ Üç üreticide de `max_tokens=10000`, `finish_reason=="length"` sigortası duruyor.
→ **Prompt cache doğrulandı:** 486 token'ın 384'ü cache hit (%79) — sistem
promptu sabit tutulmalı.

### 5.4 SQLite tuzakları
- **`crud.add_log` çağıranın işlemini commit eder.** Ayrı oturuma taşımak
  denendi: SQLite tek-yazar kilidi yüzünden loglar **sessizce düşüyordu**
  (tam da hata anında). Geri alındı; yarım iş varken log yazan taraf
  **önce `db.rollback()`** yapar (`scheduler/manager.py::_safe`).
- **`SessionLocal` autoflush=False.** Aynı çağrıda `db.add()` edilen kayıtlar
  sorguda GÖRÜNMEZ. `enqueue_events` bu yüzden her olayı thread kökü
  sanıyordu; yerel `set` ile çözüldü.
- DB **WAL + busy_timeout=20000** ile açılır (scheduler thread'leri + web
  istekleri eşzamanlı erişiyor).

### 5.5 Zaman dilimi
"Bugün" UTC gününe göre hesaplanıyordu. Türkiye UTC+3 olduğu için gece
21:00'den sonra akşam maçları "bugün" sayılmıyordu — bütçe planlayıcı 0 maç
görüyordu. → `app/core/timeutil.py`.

### 5.6 Küçük ama can sıkıcı olanlar
- **Jinja:** sözlük anahtarı `items` olunca `g.items` sözlüğün `.items()`
  metoduna çözülüyor → `variants` olarak yeniden adlandırıldı.
- **PowerShell `Set-Content` Türkçe karakterleri bozdu** (ANSI okuyup UTF-8
  yazdı). `git checkout` ile geri alındı. **Kaynak dosyalarını PowerShell ile
  yazma, Edit aracını kullan.**
- **X çok genel metinleri sessizce reddediyor** (boş `tweet_results{}`).
  Canlı test ederken benzersiz metin kullan.
- Thread'de **yanıt kökünden önce gönderilemez** — gol önceliği 1, başlama
  düdüğü 3 olduğu için thread bölünüyordu. Kök iptal olmuşsa yanıt köke
  terfi eder.

---

## 6. Nasıl çalıştırılır

```powershell
# Normal başlatma
cd C:\xampp\htdocs\futbol-python
.\.venv\Scripts\python.exe run.py

# Kolay yol (çökerse otomatik yeniden başlar)
start_bot.bat

# 7/24 (yönetici PowerShell) — bilgisayar açılınca otomatik
.\install_service.ps1
Start-ScheduledTask -TaskName FutbolBot
```
Panel: **http://127.0.0.1:8000** (yalnız yerel erişim)

**Bağımlılıklar:** Python 3.12 (`C:\Program Files\Python312`), `.venv`,
`requirements.txt`. Sırlar `data/` altında Fernet ile şifreli — git'e girmez.

---

## 6.5 İLK CANLI MAÇ TESTİ — 17.08 akşamı (Samsunspor-Göztepe)

Bot ilk kez gerçek bir maçı anlattı. **Simülasyonla bulunamayacak 6 hata**
çıktı, hepsi maç sırasında düzeltildi. Ardından **X hesabı otomatik davranış
bayrağı yedi** ve paylaşım durduruldu.

### Bulunan ve düzeltilen hatalar
| # | Hata | Belirti |
|---|---|---|
| 1 | `inGamePenalty/awarded` gol sanıldı | "Penaltıdan gole kavuştu! 0-1" — **gol yoktu**, takım da yanlıştı. Tweet silindi. Gerçek penaltı golleri `goal`+`penalty` olarak gelir. |
| 2 | Aktif saatler **sunucu yerel saatine** bağlıydı | Sunucu UTC-7. 8-24 ayarı fiilen "TR 18:00-09:59 yayın, TR 10:00-17:59 sessizlik" demekti — bot Türk gündüzü susup gece tweetliyordu. 4.5 saatlik yayın boşluğu ve bayatlayıp iptal olan 2 alıntı bu yüzdendi. |
| 3 | Ritim şeritleri birbirini kilitliyordu | Tek `next_allowed_at` vardı; ana akış tweeti (90-400 sn) thread şeridini de kilitliyordu. 3. dakika golü dakikalarca bekledi. Artık `last_post_at` + **gönderilecek** tweetin şeridine göre. |
| 4 | Goller thread'e gömülüyordu | X'te kendi tweetine verilen yanıtlar profil ana akışında **görünmüyor**. Tüm maç içeriği görünmez kaldı. Goller/kırmızı kart/maç sonu artık ana akışa çıkıyor. |
| 5 | Devre arası kaçacaktı | Kaynak `"HT"` değil `"First half"` gönderiyor. Artık maçın **durum geçişinden** (`live↔ht`) türetiliyor. |
| 6 | Negatif dakika + mükerrer olay | `dk=-5` geldi, LLM "5. dakikada" yazdı. Dakika normalizasyonu değişince dedup anahtarı da değişip aynı kart iki kez atıldı. Anahtar artık **ham** dakikaya dayanıyor; bilgisiz olaylar hiç üretilmiyor. |

### Doğrulanan özellikler
- Thread zinciri, maç etiketi (`#SAMvGÖZ`), kapsam kademeleri, sessiz-ret koruması
- **İstatistikli devre arası** — her iddiası gerçek veriyle doğrulandı:
  *"Topla oynama %66'da; Göztepe kornerlerde 7-1 üstün ama skor tabelada
  ev sahibinin"* (kaynak: %66-34, korner 1-7). Sabahki uydurma anlatının
  yerini aldı.

### ⚠️ KOD 226 — X otomatik davranış bayrağı
`22:08`'de X gönderimi reddetti:
> `code 226 · Permissions` — "This request looks like it might be automated."

**Askıya alınma değil**, yumuşak blok. Ölçümler: bugün **36 tweet**, son
1 saatte **11**, son 15 dk'da **3** — ve hesabın 7 takipçisi var. X'in
gözünde tipik bot imzası. Sabahki iki "boş yanıt" olayı da muhtemelen
aynı sistemin erken uyarısıydı.

**Alınan aksiyon:** paylaşım + kaynak alıntıları kapatıldı, veri toplama
sürdü. Tempo ve kapsam kalıcı olarak düşürüldü:

| Ayar | Önce | Sonra |
|---|---|---|
| Ana akış aralığı | 90–400 sn | 300–900 sn |
| Thread aralığı | 15–45 sn | 45–120 sn |
| Günlük tavan | 220 | **70** |
| Haber/saat · Alıntı/saat | 2 · 12 | 1 · 3 |

Kapsam: Süper Lig `full`, UCL `standard`, 1.Lig+UEL `minimal`, kalan 5 lig
`result_only`. Tahmini yeni hacim: benzer bir günde ~20-25 tweet.

**Ders:** teknik olarak doğru çalışmak yetmiyor; X'in gözünde de doğal
görünmek gerekiyor. Hacim ve düzenlilik, doğruluktan daha riskli.

---

## 6.6 İKİNCİ AKŞAM — 18.08 (Faz 2 görseller + hesap kısıtlaması)

### Görsel üretimi canlıda kanıtlandı (Wiro.ai)
- Gol / devre arası / maç sonu kartları **enerjik stilde** (3D metalik yazı,
  stadyum ışıkları, takım renkleri) otomatik üretiliyor. Kullanıcı sade/
  editoryal yönü reddetti, enerjik olanı seçti.
- İlk canlı gol kartı: **GOL! / MASON GREENWOOD 47' / FENERBAHÇE 1-1 LYON** —
  metin kusursuz, X'te fotoğraf olarak eklendi.
- Zincir: olay → DeepSeek metni → Wiro kartı → X'e görselli tweet, ~2 dk.
- Hız güvencesi: gol görseli en fazla 4 dk bekler, yetişmezse metin gider.
- AI ara sıra küçük etikette harf kaçırıyor ("MAÇ SONURCU") — kullanıcı
  kabul etti. Büyük metin (skor) neredeyse hep doğru.

### Düzeltilen canlı hatalar (18.08)
- **Dispatcher şerit tıkanması:** `_pick_post` tek post seçip onun şeridi
  hazır değilse tüm gönderimi durduruyordu; hazır bekleyen hızlı-şerit
  tweetleri (devre arası kartları) kilitliyordu. Ritim kontrolü seçimin
  içine alındı.
- **Çift `ht` olayı:** `_add_event` flush etmiyordu, aynı turda durum+incident
  yolları çift 'ht' üretiyordu → flush eklendi.
- **Otomatik alıntı** yayını açarken yanlışlıkla True kalmıştı → kapatıldı.

### ⚠️⚠️ KOD 344 — GÜNLÜK LİMİT + HESAP KISITLAMASI
`23:23`'te kod 344: "You have reached your daily limit for sending Tweets."
Askıya alınma DEĞİL, X'in günlük kotası. AMA kritik bulgu: limit **yalnızca
12 tweette** doldu. Normal hesap ~2400/gün atabilir. Yani **17.08'deki 226
bayrağı X'i bu hesabın günlük hakkını ciddi kısmaya itmiş** — hesap gözetim
altında ve throttled.

**Alınan aksiyon (kalıcı):**
- Kod 344 otomatik yönetimi: takılınca durur, **ertesi gün kota sıfırlanınca
  health işi otomatik açar** (226'dan farkı bu — 226 elle açılır).
  `daily_limit_hit` ayarı sadece 344'te yazılır.
- Günlük tavan **10**'a, aralık **10-30 dk**'ya indirildi — X'in throttled
  limitinin altında kalıp sürekli 344 yememek için.

**Ders:** Bu hesap artık riskli. Bir 226 daha kalıcı ban getirebilir.
Yeni hesap hazırlanmalı; mevcut hesap düşük tempoda + elle etkileşimle
"ısıtılmalı".

---

## 7. Nerede kaldık / sıradakiler

### 🔴 ÖNCELİKLİ — paylaşım şu an KAPALI
- [ ] X bloğunun kalkmasını bekle (genelde birkaç saat–1 gün).
      **Hesaplar** sayfasından "Doğrula" → yeşil "hazır" görünce
      Genel Bakış'tan paylaşımı aç.
- [ ] Açtıktan sonra ilk gün hacmi izle; 70 tavanına yaklaşıyorsa daha da düşür.
- [ ] Hesabı **elle de kullan** (beğeni, yanıt, manuel tweet) — tamamen
      düzenli makine davranışı bayrak çekiyor.

### Kullanıcının kararına bırakılan
- [ ] **Otomatik alıntı** `@beINSPORTS_TR` için KAPALI (226 sonrası kapatıldı).
- [ ] Bekleyen küçük iyileştirmeler: maç öncesi/ilk 11 tweetlerinde anlamsız
      "0-0" ibaresi; ilk 11 tweetinde oyuncu adlarının yer almaması
      (280 karaktere sığmıyor — thread'e bölünebilir).

### Doğrulanmış / artık gerekmeyen
- ✅ Canlı maç anlatımı gerçek maçta test edildi (bkz. § 6.5)
- ✅ Günlük işler doğal saatinde çalıştı (10:00 fikstür, 12:00 puan durumu)
- ✅ İstatistikli devre arası gerçek veriyle doğrulandı

### Hâlâ açık sorular
- X'in aralıklı **boş yanıt** (sessiz ret) davranışının kesin nedeni.
  226 bayrağıyla ilişkili olabilir; koruma var (yeniden dener) ama kök
  neden bilinmiyor.
- SofaScore skor akışı incident akışından ~4 dk önce güncelleniyor;
  skor değişip gol kaydı henüz düşmemiş olabiliyor.

### Bilinen sınırlar
- Maç etiketi (#BJKvEYP) yayıncı paylaşana kadar öğrenilmiyor; maç öncesi
  tweetlerde etiket olmayabilir.
- Kaynak eşleştirme takım adı/etikete dayalı; ikisi de yoksa maça bağlanmaz
  (gerçek veride 20'de 15 başarı).
- Tek hesap var; rotasyonun asıl faydası ikinci hesap eklenince ortaya çıkar.

---

## 8. Commit geçmişi (özet)

| Commit | İçerik |
|---|---|
| `4d1b3f3` | Tüm fazlar: iskelet, veri, X, içerik, scheduler, panel |
| `d979063` | İşlem izolasyonu + SQLite eş zamanlılık |
| `6ce2dce` | Haber motoru, 143 şablon, twikit yaması |
| `535c7d1` | Tweet gönderimi düzeltildi (mükerrer tweet) |
| `29b73a5` | Thread anlatımı, kapsam kademeleri, tam metinli haberler |
| `5e42ec4` | Akıllı bütçe planlayıcı + arayüz yeniden yazıldı |
| `2c6aa2a` | İzlenen hesaplar (yayıncı) |
| `3e2bc24` | Alıntı kartı kaldırıldı → video linki |
| `d2bfb5e` | Satır düzeni + marka etiketi temizliği |
| `8d003d3` | Fotoğraflı kaynaklar → görselin kendisi |
| `5fa1411` `89bcae1` | DeepSeek token bütçesi |
| `87bfaf5` | Tanıtım gönderileri elendi |
| `5f518ce` | Önem sıralaması + tavan 12 |
