Tweetlerini eklentisiz toplu silme

Eski X paylaşımlarınızda temizlik yapmak için Chrome konsoluna yapıştırılacak ücretsiz bir betik: önce simülasyon, son tweetler korunur, sınırlı gruplar halinde silme. Eklenti yok, üçüncü taraf hizmet yok, hiçbir şey tarayıcınızdan çıkmaz.

Başlamadan önce: bir tweetin silinmesi kalıcıdır. Betik simülasyon modunda sunulur (DRY_RUN = true): neyin silineceğini hiçbir şeye dokunmadan listeler. Önce X arşivinizi indirin, simülasyonu kontrol edin ve ancak ondan sonra gerçek silmeye geçin. Arayüzün otomatikleştirilmesi X'in kullanım koşullarında öngörülmemiştir: yavaş ilerleyin, kendi sorumluluğunuzda.

İzlenecek adımlar

  1. Profilinizi açın: hesabınıza giriş yapmış olarak x.com/kullanici_adin adresine, Gönderiler sekmesine gidin.
  2. Konsolu açın: F12 tuşuna basın (Mac'te Cmd+Option+J), ardından Console sekmesine tıklayın.
  3. Betiği ayarlayın: aşağıdaki kodu kopyalayın, kullanici_adin yerine X kullanıcı adınızı (@ olmadan) yazın, KEEP_RECENT değerini (korunacak son tweet sayısı) ayarlayın ve gerekirse asla silinmeyecek tweetlerin kimlik numaralarını KEEP_STATUS_IDS içine yazın.
  4. Simüle edin: betiği konsola yapıştırın ve Enter ile onaylayın. DRY_RUN = true (varsayılan ayar) ile hiçbir şey silmeden, silinecek tweetlerin listesini gösterir.
  5. Silin: simülasyon size uygunsa DRY_RUN değerini false yapın ve yeniden çalıştırın. Betik her çalıştırmada en fazla 40 tweet siler, 6 ile 14 saniye arasında molalarla.
  6. Tekrarlayın: geri kalanı gruplar halinde işlemek için betiği gerektiği kadar yeniden çalıştırın.

Betik

Chrome konsolu · x.com
(() => {
  // ============ AYARLAR ============
  const HANDLE          = "kullanici_adin"; // X kullanici adiniz, @ olmadan
  const KEEP_RECENT     = 15;           // en son 15 tweeti ASLA silme (en ustte olanlar)
  const KEEP_STATUS_IDS = [             // EK koruma (istege bagli): asla silinmeyecek kimlik numaralari
    "1234567890123456789",
  ];
  let   DRY_RUN         = true;         // true = simulasyon (sadece liste). false = GERCEK SILME.
  const MIN_DELAY_MS    = 6000;
  const MAX_DELAY_MS    = 14000;
  const MAX_DELETIONS   = 40;           // calistirma basina ust sinir (devam icin yeniden calistir)
  // ================================

  const me = HANDLE.toLowerCase();
  const KEEP = new Set(KEEP_STATUS_IDS.map(String));
  const protectedRecent = new Set();
  const RX_DELETE = /(Sil|delete|supprimer)/i;
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
  const rand  = (a, b) => Math.floor(a + Math.random() * (b - a));
  const skipped = new Set();

  if (!location.pathname.toLowerCase().startsWith("/" + me)) {
    console.warn(`%c[DUR] https://x.com/${HANDLE} adresine (Gonderiler sekmesi) git ve yeniden calistir.`,
      "color:#e0245e;font-weight:bold");
    return;
  }

  const articles = () => [...document.querySelectorAll('article[data-testid="tweet"]')];

  const idOf = (art) => {
    const href = [...art.querySelectorAll('a[href*="/status/"]')]
      .map((x) => x.getAttribute("href"))
      .find((h) => new RegExp(`^/${HANDLE}/status/\\d+`, "i").test(h));
    if (!href) return null;
    const m = href.match(/status\/(\d+)/);
    return m ? m[1] : null;
  };

  // En son KEEP_RECENT tweeti (en ustte olanlari) yukle ve koru.
  async function protectRecent() {
    const order = [], seen = new Set();
    let noNew = 0;
    window.scrollTo(0, 0);
    await sleep(1200);
    while (order.length < KEEP_RECENT && noNew < 6) {
      let added = 0;
      for (const art of articles()) {
        const id = idOf(art);
        if (id && !seen.has(id)) { seen.add(id); order.push(id); added++; }
      }
      noNew = added ? 0 : noNew + 1;
      if (order.length >= KEEP_RECENT) break;
      const cs = articles();
      (cs[cs.length - 1] || document.body).scrollIntoView({ block: "end" });
      await sleep(1400);
    }
    for (const id of order.slice(0, KEEP_RECENT)) protectedRecent.add(id);
    return order.slice(0, KEEP_RECENT);
  }

  const eligible = (art) => {
    const id = idOf(art);
    if (!id) return null;
    if (protectedRecent.has(id)) return null;   // en son tweetlerden biri
    if (KEEP.has(id)) return null;              // ek olarak korunan kimlik
    if (skipped.has(id)) return null;
    return id;
  };

  async function deleteOne(art, id) {
    const caret = art.querySelector('[data-testid="caret"]');
    if (!caret) { skipped.add(id); return false; }
    caret.click();
    await sleep(700);
    const del = [...document.querySelectorAll('[role="menuitem"]')]
      .find((i) => RX_DELETE.test((i.innerText || "").trim()));
    if (!del) { document.body.click(); skipped.add(id); return false; }
    del.click();
    await sleep(700);
    const confirmBtn = document.querySelector('[data-testid="confirmationSheetConfirm"]');
    if (!confirmBtn) { skipped.add(id); return false; }
    confirmBtn.click();
    await sleep(1500);
    return true;
  }

  async function dryScan() {
    const seen = new Set();
    let noNew = 0;
    console.log("%c[SIMULASYON] Hicbir sey silinmeyecek. Sayiliyor...", "color:#1d9bf0;font-weight:bold");
    while (noNew < 4) {
      let added = 0;
      for (const art of articles()) {
        const id = eligible(art);
        if (id && !seen.has(id)) { seen.add(id); added++; }
      }
      noNew = added ? 0 : noNew + 1;
      const cs = articles();
      (cs[cs.length - 1] || document.body).scrollIntoView({ block: "end" });
      await sleep(1200);
    }
    console.log(`%c[SIMULASYON] ${seen.size} tweet SILINIRDI (en son olanlar korunuyor).`,
      "color:#1d9bf0;font-weight:bold");
    console.log([...seen].slice(0, 60));
  }

  async function run() {
    let count = 0, guard = 0, noHit = 0, started = false;
    while (count < MAX_DELETIONS && guard++ < 1500) {
      const pair = articles().map((a) => [a, eligible(a)]).find(([, id]) => id);
      if (!pair) {
        if ((started && noHit >= 6) || noHit >= 40) break;
        const cs = articles();
        (cs[cs.length - 1] || document.body).scrollIntoView({ block: "end" });
        await sleep(1500);
        noHit++;
        continue;
      }
      started = true; noHit = 0;
      const [node, id] = pair;
      node.scrollIntoView({ block: "center" });
      await sleep(600);
      const ok = await deleteOne(node, id);
      if (ok) count++;
      console.log(`${ok ? "silindi" : "atlandi"} ${id}  (toplam: ${count}/${MAX_DELETIONS})`);
      await sleep(rand(MIN_DELAY_MS, MAX_DELAY_MS));
    }
    console.log(`%c[BITTI] Bu turda ${count} silindi. Kalan varsa betigi yeniden calistir.`,
      "color:#00ba7c;font-weight:bold");
  }

  (async () => {
    console.log(`En son ${KEEP_RECENT} tweet yukleniyor ve korunuyor...`);
    const recent = await protectRecent();
    if (recent.length < KEEP_RECENT) {
      console.warn(`%c[DUR] Yalnizca ${recent.length} tweet yuklendi (X hiz sinirladi). Sayfayi yenile (F5) ve tekrar dene.`,
        "color:#e0245e;font-weight:bold");
      return;
    }
    console.log("%cKorunanlar (en son olanlar):", "color:#00ba7c;font-weight:bold", recent);
    if (DRY_RUN) { await dryScan(); return; }
    await run();
  })();
})();

Betik sizi nasıl korur

Dört güvenlik önlemi yerleşik olarak bulunur. Önce varsayılan simülasyon: DRY_RUN değeri true olduğu sürece hiçbir şey silinmez, yalnızca ilgili tweetlerin listesini alırsınız. Ardından son tweetlerin korunması: betik önce en son tweetlerinizi (varsayılan 15) ezberler ve onlara asla dokunmaz. Sonra beyaz liste: KEEP_STATUS_IDS içine konan her kimlik numarası dokunulmazdır; sabitlenmiş tweetleriniz veya duyurularınız için pratik. Son olarak ihtiyatlı ritim: her silme arasında 6 ile 14 saniye rastgele molalar ve X'in sınırlamalarını tetiklememek için 40'tan sonra otomatik durma.

Büyük temizlikten önce önemli olanı arşivlemeyi unutmayın: e-posta ve URL çıkarıcı bir tweet dışa aktarımındaki tüm bağlantıları toplar, kelime sayacı yıllarca süren paylaşımlarınızın ne kadar tuttuğunu söyler ve metin temizleyici bir arşiv kopyala-yapıştırını düzene sokar. Gelecekteki tweetleriniz içinse stilize Unicode metin ve büyük küçük harf dönüştürücü yanınızda. Araç kutusundaki tüm araçlar, bu kılavuz gibi, gizlilik politikamızın ruhuna uygun olarak tarayıcınızda çalışır.

Sık sorulan sorular

Silme işlemi geri alınabilir mi?

Hayır. Silinen bir tweet kalıcı olarak silinir; X herhangi bir geri dönüşüm kutusu sunmaz. Bu yüzden betik simülasyon modunda sunulur (DRY_RUN = true): önce olduğu gibi çalıştırıp neyin silineceğinin listesini alın, kontrol edin ve ancak emin olduğunuzda false yapın. Ayrıca öncesinde X arşivinizi indirmeyi unutmayın (Ayarlar, ardından Hesabınız, ardından Verilerinizin bir arşivini indirin).

Betik başkasının tweetlerini silebilir mi?

Hayır. X, Sil düğmesini yalnızca kendi paylaşımlarınızda gösterir: betik yalnızca oturum açtığınız hesap üzerinde, kendi profil sayfanızda işlem yapabilir. Ne başkalarının yanıtlarına ne de herhangi bir üçüncü taraf içeriğine dokunur.

Neden 40 silme sınırı ve rastgele gecikmeler?

Platforma karşı nazik kalmak için. Çok hızlı ya da çok toplu silmeler X'in sınırlamalarını, hatta hesabın geçici olarak engellenmesini tetikleyebilir. Betik her silme arasında 6 ile 14 saniye bekler ve 40'tan sonra durur: gruplar halinde devam etmek için yeniden çalıştırın.

Bu X tarafından izin verilen bir şey mi?

Kendi içeriğinizi siliyorsunuz, bu sizin hakkınız. Buna karşılık web arayüzünün otomatikleştirilmesi X'in kullanım koşullarında öngörülmemiştir: bu betiği ölçülü ve kendi sorumluluğunuzda kullanın. Resmi alternatif, elle silme ya da API üzerinden çalışan özel hizmetlerdir.

Belirli tweetler nasıl korunur?

İki koruma bir arada çalışır: KEEP_RECENT en son tweetlerinizi otomatik olarak korur (varsayılan 15) ve KEEP_STATUS_IDS asla silinmeyecek kimlik numaralarından oluşan bir liste kabul eder. Bir tweetin kimlik numarası adresinin sonundaki büyük sayıdır: x.com/kullanici/status/1234567890123456789.