अपने ट्वीट थोक में हटाएँ, बिना किसी एक्सटेंशन के

अपने पुराने X पोस्ट की सफ़ाई के लिए Chrome कंसोल में चिपकाने वाली एक मुफ़्त स्क्रिप्ट : पहले सिमुलेशन, हाल के ट्वीट सुरक्षित, सीमित बैच में हटाना। कोई एक्सटेंशन नहीं, कोई तीसरी सेवा नहीं, कुछ भी आपके ब्राउज़र से बाहर नहीं जाता।

शुरू करने से पहले : किसी ट्वीट का हटाया जाना स्थायी होता है। स्क्रिप्ट सिमुलेशन मोड (DRY_RUN = true) में दी गई है : यह बिना कुछ छुए सिर्फ़ यह सूची दिखाती है कि क्या हटाया जाता। पहले अपना X आर्काइव डाउनलोड करें, सिमुलेशन जाँचें, और उसके बाद ही असली हटाने पर जाएँ। इंटरफ़ेस का स्वचालन X की उपयोग शर्तों में शामिल नहीं है : धीरे-धीरे चलें, अपने जोखिम पर।

करने का तरीक़ा

  1. अपना प्रोफ़ाइल खोलें : अपने खाते से लॉग-इन रहते हुए x.com/aapka_username पर जाएँ, Posts टैब में।
  2. कंसोल खोलें : F12 दबाएँ (या Mac पर Cmd+Option+J), फिर Console टैब पर क्लिक करें।
  3. स्क्रिप्ट सेट करें : नीचे दिया कोड कॉपी करें, aapka_username की जगह अपना X पहचान-नाम (@ के बिना) डालें, KEEP_RECENT (कितने हाल के ट्वीट रखने हैं) समायोजित करें और ज़रूरत हो तो KEEP_STATUS_IDS में उन ट्वीट के पहचान-नंबर सूचीबद्ध करें जिन्हें कभी नहीं हटाना है।
  4. सिमुलेट करें : स्क्रिप्ट को कंसोल में चिपकाएँ और Enter दबाएँ। DRY_RUN = true (डिफ़ॉल्ट सेटिंग) के साथ यह उन ट्वीट की सूची दिखाती है जो हटाए जाते, बिना कुछ हटाए।
  5. हटाएँ : अगर सिमुलेशन आपको ठीक लगे, तो DRY_RUN को false करें और फिर से चलाएँ। स्क्रिप्ट हर बार अधिकतम 40 ट्वीट हटाती है, बीच में 6 से 14 सेकंड के ठहराव के साथ।
  6. दोहराएँ : बचे हुए ट्वीट बैच में निपटाने के लिए स्क्रिप्ट को जितनी बार ज़रूरत हो फिर से चलाएँ।

स्क्रिप्ट

Chrome कंसोल · x.com
(() => {
  // ============ CONFIG ============
  const HANDLE          = "aapka_username"; // aapka X pehchan-naam, @ ke bina
  const KEEP_RECENT     = 15;           // sabse haal ke 15 tweet (upar wale) KABHI nahi hatega
  const KEEP_STATUS_IDS = [             // EXTRA suraksha (vaikalpik) : jin ID ko kabhi nahi hatana
    "1234567890123456789",
  ];
  let   DRY_RUN         = true;         // true = simulation (sirf suchi). false = ASLI DELETE.
  const MIN_DELAY_MS    = 6000;
  const MAX_DELAY_MS    = 14000;
  const MAX_DELETIONS   = 40;           // har run ki seema (aage ke liye phir chalayein)
  // ================================

  const me = HANDLE.toLowerCase();
  const KEEP = new Set(KEEP_STATUS_IDS.map(String));
  const protectedRecent = new Set();
  const RX_DELETE = /(हटा|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[STOP] https://x.com/${HANDLE} (Posts tab) par jaayein, phir dobara chalayein.`,
      "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;
  };

  // KEEP_RECENT sabse haal ke tweet (upar wale) load aur surakshit karta hai.
  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;   // sabse haal walon mein se ek
    if (KEEP.has(id)) return null;              // extra surakshit ID
    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[SIMULATION] Kuch nahi hatega. Ginti ho rahi hai...", "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[SIMULATION] ${seen.size} tweet HATEGA (sabse haal wale surakshit hain).`,
      "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 ? "hataya" : "chhoda"} ${id}  (kul: ${count}/${MAX_DELETIONS})`);
      await sleep(rand(MIN_DELAY_MS, MAX_DELAY_MS));
    }
    console.log(`%c[SAMAPT] Is baar ${count} hataye. Baaki bache hon to script phir chalayein.`,
      "color:#00ba7c;font-weight:bold");
  }

  (async () => {
    console.log(`Sabse haal ke ${KEEP_RECENT} tweet load aur surakshit ho rahe hain...`);
    const recent = await protectRecent();
    if (recent.length < KEEP_RECENT) {
      console.warn(`%c[STOP] Sirf ${recent.length} tweet load hue (X ne throttle kiya). Page reload karein (F5) aur dobara koshish karein.`,
        "color:#e0245e;font-weight:bold");
      return;
    }
    console.log("%cSurakshit (sabse haal wale):", "color:#00ba7c;font-weight:bold", recent);
    if (DRY_RUN) { await dryScan(); return; }
    await run();
  })();
})();

स्क्रिप्ट आपको कैसे सुरक्षित रखती है

इसमें चार सुरक्षा-कवच बने हुए हैं। सबसे पहले डिफ़ॉल्ट सिमुलेशन : जब तक DRY_RUN का मान true है, कुछ नहीं हटता, आपको सिर्फ़ संबंधित ट्वीट की सूची मिलती है। फिर हाल के ट्वीट की सुरक्षा : स्क्रिप्ट सबसे पहले आपके सबसे हाल के ट्वीट (डिफ़ॉल्ट 15) याद कर लेती है और उन्हें कभी नहीं छूती। इसके बाद सुरक्षित सूची : KEEP_STATUS_IDS में डाला हर पहचान-नंबर अछूता रहता है, आपके पिन किए ट्वीट या घोषणाओं के लिए बढ़िया। और आख़िर में सावधान रफ़्तार : हर हटाने के बीच 6 से 14 सेकंड के यादृच्छिक ठहराव और 40 के बाद अपने आप रुक जाना, ताकि X की सीमाएँ न लगें।

बड़ी सफ़ाई से पहले, जो ज़रूरी है उसे संभालकर रख लें : ई-मेल और URL एक्सट्रैक्टर आपके ट्वीट एक्सपोर्ट के सारे लिंक निकाल लेता है, शब्द काउंटर बताता है कि आपके सालों के पोस्ट का वज़न कितना था, और टेक्स्ट क्लीनर आर्काइव के कॉपी-पेस्ट को साफ़-सुथरा कर देता है। और आपके आने वाले ट्वीट के लिए यूनिकोड स्टाइलिश टेक्स्ट और केस कन्वर्टर हाज़िर हैं। टूलबॉक्स के सारे टूल इसी गाइड की तरह आपके ब्राउज़र में चलते हैं, हमारी गोपनीयता नीति की भावना के अनुरूप।

सामान्य प्रश्न

क्या हटाया गया ट्वीट वापस पाया जा सकता है?

नहीं। हटाया गया ट्वीट हमेशा के लिए चला जाता है, X कोई ट्रैश या रीसायकल बिन नहीं देता। इसीलिए स्क्रिप्ट सिमुलेशन मोड (DRY_RUN = true) में दी गई है : पहले इसे वैसे ही चलाएँ ताकि उन ट्वीट की सूची मिल जाए जो हटाए जाते, उसे जाँच लें, और तभी false करें जब आप पूरी तरह पक्के हों। साथ ही पहले अपना X आर्काइव डाउनलोड करना न भूलें (सेटिंग्स, फिर आपका खाता, फिर अपने डेटा का आर्काइव डाउनलोड करें)।

क्या यह स्क्रिप्ट किसी और के ट्वीट हटा सकती है?

नहीं। X केवल आपके अपने पोस्ट पर ही हटाने का बटन दिखाता है : स्क्रिप्ट सिर्फ़ उसी खाते पर काम कर सकती है जिससे आप लॉग-इन हैं, आपके अपने प्रोफ़ाइल पेज पर। यह न किसी और के जवाबों को छूती है और न किसी तीसरे पक्ष की सामग्री को।

40 हटाने की सीमा और यादृच्छिक देरी क्यों?

प्लेटफ़ॉर्म के साथ नरमी बरतने के लिए। बहुत तेज़ या बहुत अधिक मात्रा में हटाने से X की सीमाएँ लग सकती हैं, यहाँ तक कि खाता कुछ समय के लिए ब्लॉक भी हो सकता है। स्क्रिप्ट हर हटाने के बीच 6 से 14 सेकंड रुकती है और 40 के बाद रुक जाती है : बचे हुए ट्वीट बैच में हटाने के लिए इसे फिर से चलाएँ।

क्या X इसकी अनुमति देता है?

आप अपनी ही सामग्री हटा रहे हैं, जो आपका अधिकार है। लेकिन वेब इंटरफ़ेस का स्वचालन X की उपयोग शर्तों में शामिल नहीं है : इस स्क्रिप्ट का इस्तेमाल संयम से और अपने जोखिम पर करें। आधिकारिक विकल्प है हाथ से हटाना, या API के ज़रिए काम करने वाली समर्पित सेवाएँ।

कुछ ख़ास ट्वीट कैसे सुरक्षित रखें?

दो सुरक्षाएँ एक साथ काम करती हैं : KEEP_RECENT आपके सबसे हाल के ट्वीट अपने आप रखता है (डिफ़ॉल्ट 15), और KEEP_STATUS_IDS ऐसे पहचान-नंबरों की सूची स्वीकार करता है जिन्हें कभी नहीं हटाना है। किसी ट्वीट का पहचान-नंबर उसके पते के अंत में लगा बड़ा अंक होता है : x.com/aapka_username/status/1234567890123456789।