حذف منشورات X القديمة باستخدام Chrome Console — دليل عربي وإنجليزي
️ هذا الدليل مخصص لحذف منشوراتك أنت فقط. الحذف نهائي، لذلك نزّل أرشيف حسابك أولاً، وابدأ بعدد صغير من المنشورات.
العربية
الفكرة
لا يوفر X زرًا رسميًا لحذف جميع المنشورات دفعة واحدة. يمكن استخدام سكربت داخل Chrome Console لفتح قائمة كل منشور، اختيار حذف، تأكيد العملية، ثم الانتقال إلى المنشور التالي.
السكربت أدناه مضبوط افتراضيًا على حذف 5 منشورات فقط. بعد نجاح الاختبار، يمكن رفع قيمة MAX_DELETIONS تدريجيًا.
قبل البدء
- نزّل أرشيف حسابك من إعدادات X.
- افتح صفحة ملفك الشخصي في X، وتأكد أنك داخل تبويب Posts.
- افتح أدوات المطور:
- macOS:
Cmd + Option + J - Windows/Linux:
Ctrl + Shift + J
- macOS:
- الصق السكربت كاملًا في Console واضغط Enter.
- راقب الرسائل التي تبدأ بـ
✅ Deleted.
السكربت الكامل
(async () => {
const DRY_RUN = false;
const MAX_DELETIONS = 5;
const MENU_OPEN_DELAY_MS = 900;
const CONFIRM_DIALOG_DELAY_MS = 900;
const AFTER_DELETE_DELAY_MS = 2500;
const SCROLL_DELAY_MS = 2200;
const MAX_EMPTY_SCROLLS = 10;
const MAX_FAILURES = 10;
window.stopXDelete = false;
const sleep = (ms) =>
new Promise((resolve) => setTimeout(resolve, ms));
const getText = (element) =>
(element?.innerText || element?.textContent || "")
.trim()
.replace(/\s+/g, " ")
.toLowerCase();
const isVisible = (element) => {
if (!element) return false;
const style = window.getComputedStyle(element);
const rect = element.getBoundingClientRect();
return (
style.display !== "none" &&
style.visibility !== "hidden" &&
Number(style.opacity) !== 0 &&
rect.width > 0 &&
rect.height > 0
);
};
const isDeleteText = (text) => {
const value = text.trim().toLowerCase();
return (
value === "delete" ||
value === "delete post" ||
value.includes("delete post") ||
value === "حذف" ||
value.includes("حذف المنشور") ||
value.includes("حذف التغريدة")
);
};
const findDeleteMenuItem = () => {
const selectors = [
class="token string">'[role="menuitem"]',
class="token string">'[data-testid="Dropdown"] [role="button"]',
class="token string">'[data-testid="Dropdown"] div[role="menuitem"]',
];
const candidates = [
...document.querySelectorAll(selectors.join(",")),
];
return candidates.find(
(element) =>
isVisible(element) &&
isDeleteText(getText(element))
);
};
const findConfirmButton = () => {
const directSelectors = [
class="token string">'[data-testid="confirmationSheetConfirm"]',
class="token string">'[role="dialog"] [data-testid="confirmationSheetConfirm"]',
];
for (const selector of directSelectors) {
const button = document.querySelector(selector);
if (button && isVisible(button)) {
return button;
}
}
const dialog = document.querySelector(class="token string">'[role="dialog"]');
if (!dialog) return null;
const candidates = [
...dialog.querySelectorAll(
class="token string">'button, [role="button"], [data-testid]'
),
];
return candidates.find(
(element) =>
isVisible(element) &&
isDeleteText(getText(element))
);
};
const closeMenuByClickingOutside = async () => {
const menu =
document.querySelector(class="token string">'[role="menu"]') ||
document.querySelector(class="token string">'[data-testid="Dropdown"]');
if (!menu) return;
const points = [
[10, 10],
[window.innerWidth - 10, 10],
[10, window.innerHeight - 10],
];
for (const [x, y] of points) {
const target = document.elementFromPoint(x, y);
if (target && !menu.contains(target)) {
target.click();
await sleep(350);
return;
}
}
const main = document.querySelector("main");
if (main && !menu.contains(main)) {
main.click();
await sleep(350);
}
};
const getPostPreview = (post) =>
getText(post).slice(0, 180);
const getVisiblePosts = () =>
[...document.querySelectorAll(class="token string">'article[data-testid="tweet"]')]
.filter(isVisible);
let deleted = 0;
let previewed = 0;
let inspected = 0;
let failures = 0;
let emptyScrolls = 0;
const processedPostLinks = new Set();
console.log(
DRY_RUN
? "🔎 DRY RUN started: nothing will be deleted."
: class="token string">`🗑️ Deletion started. Maximum: ${MAX_DELETIONS}`
);
while (
!window.stopXDelete &&
deleted < MAX_DELETIONS &&
emptyScrolls < MAX_EMPTY_SCROLLS &&
failures < MAX_FAILURES
) {
const posts = getVisiblePosts();
let foundActionablePost = false;
for (const post of posts) {
if (window.stopXDelete || deleted >= MAX_DELETIONS) {
break;
}
const statusLink = post.querySelector(class="token string">'a[href*="/status/"]');
const postKey = statusLink?.href || getPostPreview(post);
if (processedPostLinks.has(postKey)) {
continue;
}
processedPostLinks.add(postKey);
inspected += 1;
const menuButton =
post.querySelector(class="token string">'[data-testid="caret"]') ||
post.querySelector(class="token string">'[aria-label="More"]') ||
post.querySelector(class="token string">'[aria-label="المزيد"]');
if (!menuButton || !isVisible(menuButton)) {
continue;
}
post.scrollIntoView({
block: "center",
behavior: "auto",
});
await sleep(400);
menuButton.click();
await sleep(MENU_OPEN_DELAY_MS);
const deleteMenuItem = findDeleteMenuItem();
if (!deleteMenuItem) {
await closeMenuByClickingOutside();
continue;
}
foundActionablePost = true;
const preview = getPostPreview(post);
if (DRY_RUN) {
previewed += 1;
console.log(class="token string">`Would delete #${previewed}:`, preview);
await closeMenuByClickingOutside();
continue;
}
deleteMenuItem.click();
await sleep(CONFIRM_DIALOG_DELAY_MS);
const confirmButton = findConfirmButton();
if (!confirmButton) {
failures += 1;
console.warn(
class="token string">`⚠️ Delete confirmation button not found. Failure ${failures}/${MAX_FAILURES}`,
preview
);
await closeMenuByClickingOutside();
continue;
}
confirmButton.click();
deleted += 1;
failures = 0;
console.log(
class="token string">`✅ Deleted ${deleted}/${MAX_DELETIONS}:`,
preview
);
await sleep(AFTER_DELETE_DELAY_MS);
}
if (foundActionablePost) {
emptyScrolls = 0;
} else {
emptyScrolls += 1;
console.log(
class="token string">`Scrolling for more posts: ${emptyScrolls}/${MAX_EMPTY_SCROLLS}`
);
}
window.scrollBy({
top: Math.max(window.innerHeight * 1.7, 1200),
behavior: "smooth",
});
await sleep(SCROLL_DELAY_MS);
}
console.log("🏁 Script finished:", {
mode: DRY_RUN ? "DRY RUN" : "DELETE",
inspected,
previewed,
deleted,
failures,
stoppedManually: window.stopXDelete,
emptyScrolls,
});
})();إيقاف السكربت
الصق الأمر التالي في Console:
window.stopXDelete = true;تعديل عدد المنشورات
للبدء الآمن:
const MAX_DELETIONS = 5;بعد نجاح التجربة يمكنك تغييره إلى:
const MAX_DELETIONS = 20;أو:
const MAX_DELETIONS = 50;لا يُنصح بالقفز مباشرة إلى مئات المنشورات، لأن X قد يفرض قيودًا مؤقتة أو يغيّر بنية الصفحة.
استكشاف الأخطاء
- إذا ظهر
DRY RUN startedفلن يتم حذف شيء؛ تأكد أنDRY_RUN = false. - ظهور
Promise {طبيعي أثناء عمل السكربت.} - إذا لم يظهر زر التأكيد، حدّث الصفحة وابدأ بعدد صغير.
- إذا ظهرت أخطاء مرتبطة بـ
content.js، جرّب نافذة Incognito بعد تعطيل إضافات Chrome. - قد يتوقف السكربت إذا غيّر X أسماء العناصر أو بنية واجهته.
English
Overview
X does not provide an official button to delete every post at once. This Chrome Console script opens each post menu, selects Delete, confirms the operation, and continues to the next post.
The default configuration deletes only 5 posts. Once the test succeeds, increase MAX_DELETIONS gradually.
Before you start
- Download your X account archive.
- Open your X profile and select the Posts tab.
- Open Chrome DevTools Console:
- macOS:
Cmd + Option + J - Windows/Linux:
Ctrl + Shift + J
- macOS:
- Paste the complete script from the Arabic section above and press Enter.
- Look for console messages beginning with
✅ Deleted.
Important settings
Actual deletion must use:
const DRY_RUN = false;Start with a small limit:
const MAX_DELETIONS = 5;To stop the running script:
window.stopXDelete = true;How it works
The script:
- Detects visible posts on the profile page.
- Opens the post's overflow menu.
- Checks whether a Delete option is available.
- Clicks Delete and confirms the dialog.
- Waits between actions to reduce failures.
- Scrolls down to load more posts.
- Stops after reaching the configured limit or repeated failures.
Troubleshooting
DRY RUN startedmeans preview mode is enabled and nothing will be deleted.Promise {is normal while the asynchronous script is running.} - If confirmation cannot be found, refresh the page and retry with a smaller limit.
- If
content.jsreports an untrusted-event error, disable browser extensions or use Incognito mode. - X may change its interface at any time, which can require selector updates.
Safety notes
- Deletion is permanent.
- Keep an archive before running the script.
- Test with 3–5 posts first.
- Avoid very large batches.
- Use this only on an account you own and control.
ابدأ بخمسة منشورات، تحقق من النتيجة يدويًا، ثم ارفع العدد تدريجيًا.