Add tool to clean up facebook
This commit is contained in:
+620
@@ -0,0 +1,620 @@
|
||||
(() => {
|
||||
if (window.__fbActivityCleanerLoaded) return;
|
||||
window.__fbActivityCleanerLoaded = true;
|
||||
|
||||
const DEFAULT_SKIP =
|
||||
"tesla, ford, pokemon, pokémon, pika, pokejohn, pokemongo, pokemon go";
|
||||
const DEFAULT_OFFENSIVE =
|
||||
"kill yourself, kys, rape, nigger, nigga, faggot, retard, whore, slut, cunt, " +
|
||||
"go die, hope you die, piece of shit, fucking idiot, dumb bitch, dumbass, " +
|
||||
"get railed, shitty people, get fucked";
|
||||
|
||||
const KEEP_POST_RE =
|
||||
/profile picture|cover photo|added \d+ new photo|added a new photo|uploaded a photo|uploaded \d+ photos|posted a photo|posted \d+ photos/i;
|
||||
|
||||
const state = {
|
||||
running: false,
|
||||
mode: "comments", // comments | posts
|
||||
deleted: 0,
|
||||
skipped: 0,
|
||||
offensiveForced: 0,
|
||||
errors: 0,
|
||||
lastAction: "Idle",
|
||||
delayMs: 2500,
|
||||
failStreak: 0,
|
||||
};
|
||||
|
||||
let stopFlag = false;
|
||||
const countedSkipLabels = new Set();
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((r) => setTimeout(r, ms));
|
||||
}
|
||||
|
||||
function parseList(text) {
|
||||
return String(text || "")
|
||||
.split(/[\n,]+/)
|
||||
.map((s) => s.trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function escapeRegExp(s) {
|
||||
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
function matchesAny(haystack, terms) {
|
||||
const h = haystack || "";
|
||||
return terms.some((t) => {
|
||||
if (!t) return false;
|
||||
try {
|
||||
const re = new RegExp(`(^|[^a-z0-9])${escapeRegExp(t)}([^a-z0-9]|$)`, "i");
|
||||
return re.test(h);
|
||||
} catch {
|
||||
return h.toLowerCase().includes(t.toLowerCase());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function pageText() {
|
||||
return document.body?.innerText || "";
|
||||
}
|
||||
|
||||
function isActivityLog() {
|
||||
return /\/allactivity/.test(location.pathname + location.search) ||
|
||||
pageText().includes("Activity log");
|
||||
}
|
||||
|
||||
function detectMode() {
|
||||
const q = location.search;
|
||||
if (/MANAGEPOSTSPHOTOSANDVIDEOS/i.test(q)) return "posts";
|
||||
if (/COMMENTSCLUSTER/i.test(q)) return "comments";
|
||||
const h = pageText();
|
||||
if (/Your posts, photos and videos/i.test(h)) return "posts";
|
||||
if (/\bComments\b/.test(h) && /Activity log/i.test(h)) return "comments";
|
||||
return state.mode;
|
||||
}
|
||||
|
||||
function moreButtons() {
|
||||
// Never stamp dataset flags on these nodes — Facebook/React reuses them
|
||||
// after a delete, which made us skip every other item.
|
||||
return Array.from(
|
||||
document.querySelectorAll('[aria-label^="More options"]')
|
||||
);
|
||||
}
|
||||
|
||||
/** Tight row text only — walking too far up pulls in the whole list. */
|
||||
function rowRoot(btn) {
|
||||
return (
|
||||
btn.closest('[role="article"]') ||
|
||||
btn.closest('[role="listitem"]') ||
|
||||
btn.closest("li") ||
|
||||
btn.parentElement?.parentElement?.parentElement ||
|
||||
btn.parentElement
|
||||
);
|
||||
}
|
||||
|
||||
function rowText(btn) {
|
||||
const label = btn.getAttribute("aria-label") || "";
|
||||
const root = rowRoot(btn);
|
||||
const block = (root && root.innerText) || "";
|
||||
return `${label}\n${block.slice(0, 500)}`;
|
||||
}
|
||||
|
||||
function robustClick(el) {
|
||||
if (!el) return false;
|
||||
try {
|
||||
// Single click only — double-firing confuses Facebook and triggers errors
|
||||
if (typeof el.click === "function") el.click();
|
||||
else {
|
||||
el.dispatchEvent(
|
||||
new MouseEvent("click", { bubbles: true, cancelable: true, view: window })
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function clickText(text, root = document) {
|
||||
const els = Array.from(
|
||||
root.querySelectorAll('[role="button"], button, [role="menuitem"], span')
|
||||
);
|
||||
const el = els.find((e) => (e.innerText || "").trim() === text);
|
||||
if (el) {
|
||||
robustClick(el);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasFbError() {
|
||||
const t = pageText();
|
||||
return (
|
||||
/Something went wrong/i.test(t) ||
|
||||
/Try again later/i.test(t)
|
||||
);
|
||||
}
|
||||
|
||||
function isProcessing() {
|
||||
const t = pageText();
|
||||
return /still processing your changes/i.test(t) ||
|
||||
/We're still processing/i.test(t);
|
||||
}
|
||||
|
||||
function dismissErrorDialogs() {
|
||||
let dismissed = false;
|
||||
const dialogs = Array.from(document.querySelectorAll('[role="dialog"]'));
|
||||
for (const d of dialogs) {
|
||||
const txt = d.innerText || "";
|
||||
if (
|
||||
/Something went wrong|Please try again|Try again later/i.test(txt)
|
||||
) {
|
||||
const ok =
|
||||
Array.from(d.querySelectorAll('[role="button"], button')).find((b) =>
|
||||
/^(OK|Close|Got it|Dismiss)$/i.test((b.innerText || "").trim())
|
||||
) || d.querySelector('[aria-label="Close"]');
|
||||
if (ok) {
|
||||
robustClick(ok);
|
||||
dismissed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Toast / non-dialog banners
|
||||
if (/Try again later|Something went wrong/i.test(pageText())) {
|
||||
if (clickText("OK")) dismissed = true;
|
||||
const closes = Array.from(
|
||||
document.querySelectorAll('[aria-label="Close"]')
|
||||
);
|
||||
closes.slice(0, 3).forEach((c) => robustClick(c));
|
||||
if (closes.length) dismissed = true;
|
||||
}
|
||||
return dismissed;
|
||||
}
|
||||
|
||||
async function waitWhileProcessing(maxMs = 120000) {
|
||||
const start = Date.now();
|
||||
while (isProcessing() && !stopFlag && Date.now() - start < maxMs) {
|
||||
log("Waiting for Facebook to finish processing…");
|
||||
await sleep(3000);
|
||||
}
|
||||
}
|
||||
|
||||
async function backoffOnError(reason) {
|
||||
state.failStreak++;
|
||||
state.errors++;
|
||||
dismissErrorDialogs();
|
||||
const pauseSec = Math.min(300, 60 * state.failStreak); // 1m, 2m, 3m… max 5m
|
||||
log(`${reason} — cooling down ${pauseSec}s (streak ${state.failStreak})`);
|
||||
// Slow future deletes too
|
||||
state.delayMs = Math.min(15000, state.delayMs + 1000);
|
||||
const delayInput = document.getElementById("fb-cleaner-delay");
|
||||
if (delayInput) delayInput.value = String(state.delayMs);
|
||||
const end = Date.now() + pauseSec * 1000;
|
||||
while (!stopFlag && Date.now() < end) {
|
||||
await sleep(1000);
|
||||
}
|
||||
dismissErrorDialogs();
|
||||
}
|
||||
|
||||
async function confirmDialogIfAny() {
|
||||
await sleep(500);
|
||||
const dialog = document.querySelector('[role="dialog"]');
|
||||
if (!dialog) return true;
|
||||
const txt = dialog.innerText || "";
|
||||
if (/Something went wrong|Please try again|Try again later/i.test(txt)) {
|
||||
return false;
|
||||
}
|
||||
const btns = Array.from(dialog.querySelectorAll('[role="button"], button'));
|
||||
const conf = btns.find((b) =>
|
||||
/^(Move to Trash|Trash|Delete|Remove|Confirm)$/i.test(
|
||||
(b.innerText || "").trim()
|
||||
)
|
||||
);
|
||||
if (conf) {
|
||||
robustClick(conf);
|
||||
await sleep(900);
|
||||
return !hasFbError();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function openMenuAndAct(btn, actionLabels) {
|
||||
btn.scrollIntoView({ block: "center", behavior: "instant" });
|
||||
await sleep(300);
|
||||
robustClick(btn);
|
||||
await sleep(550);
|
||||
|
||||
let item = null;
|
||||
for (let attempt = 0; attempt < 8 && !item; attempt++) {
|
||||
const items = Array.from(document.querySelectorAll('[role="menuitem"]'));
|
||||
item = items.find((m) => {
|
||||
const t = (m.innerText || m.getAttribute("aria-label") || "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
return actionLabels.some((a) => t.includes(a.toLowerCase()));
|
||||
});
|
||||
if (!item) await sleep(180);
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
document.body.click();
|
||||
await sleep(200);
|
||||
return false;
|
||||
}
|
||||
robustClick(item);
|
||||
const confirmed = await confirmDialogIfAny();
|
||||
await sleep(1000);
|
||||
if (!confirmed || hasFbError()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function log(line) {
|
||||
state.lastAction = line;
|
||||
const el = document.getElementById("fb-cleaner-log");
|
||||
if (!el) return;
|
||||
const ts = new Date().toLocaleTimeString();
|
||||
el.textContent = `[${ts}] ${line}\n` + el.textContent.slice(0, 1800);
|
||||
updateStats();
|
||||
}
|
||||
|
||||
function updateStats() {
|
||||
const el = document.getElementById("fb-cleaner-stats");
|
||||
if (!el) return;
|
||||
el.innerHTML =
|
||||
`<div>Status: <b>${state.running ? "Running" : "Stopped"}</b> · Mode: <b>${state.mode}</b></div>` +
|
||||
`<div>Deleted: <b>${state.deleted}</b> · Skipped: <b>${state.skipped}</b></div>` +
|
||||
`<div>Offensive forced: <b>${state.offensiveForced}</b> · Errors: <b>${state.errors}</b></div>` +
|
||||
`<div>Last: ${escapeHtml(state.lastAction)}</div>`;
|
||||
}
|
||||
|
||||
function escapeHtml(s) {
|
||||
return String(s)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">");
|
||||
}
|
||||
|
||||
function classify(btn, skipTerms, offensiveTerms) {
|
||||
const label = btn.getAttribute("aria-label") || "";
|
||||
const text = rowText(btn);
|
||||
// Community skip must use the button label only — row blocks are huge and
|
||||
// include neighboring Tesla/Ford/Pokemon items, which false-skipped everything.
|
||||
const skipCommunity = matchesAny(label, skipTerms);
|
||||
const offensive = matchesAny(text, offensiveTerms);
|
||||
const keepPost = KEEP_POST_RE.test(label);
|
||||
|
||||
if (state.mode === "posts" && keepPost) {
|
||||
return { action: "skip", reason: "keep photo/profile", offensive: false };
|
||||
}
|
||||
|
||||
// Always delete offensive comments, even on Tesla/Ford/Pokemon pages
|
||||
if (offensive && state.mode === "comments") {
|
||||
return { action: "delete", reason: "offensive", offensive: true };
|
||||
}
|
||||
|
||||
if (skipCommunity) {
|
||||
return { action: "skip", reason: "skip community", offensive: false };
|
||||
}
|
||||
|
||||
return { action: "delete", reason: "cleanup", offensive: false };
|
||||
}
|
||||
|
||||
async function waitForRowGone(btn, labelBefore, maxMs = 5000) {
|
||||
const start = Date.now();
|
||||
while (!stopFlag && Date.now() - start < maxMs) {
|
||||
if (!btn.isConnected) return true;
|
||||
const current = btn.getAttribute("aria-label") || "";
|
||||
// React reused the node for a different row
|
||||
if (current && current !== labelBefore) return true;
|
||||
// Same label still present somewhere is OK if this node is gone;
|
||||
// if this node still has the label, keep waiting briefly
|
||||
await sleep(200);
|
||||
}
|
||||
return !btn.isConnected || (btn.getAttribute("aria-label") || "") !== labelBefore;
|
||||
}
|
||||
|
||||
async function processOne(skipTerms, offensiveTerms) {
|
||||
if (hasFbError()) {
|
||||
await backoffOnError("Facebook error");
|
||||
return "rate-limit";
|
||||
}
|
||||
await waitWhileProcessing();
|
||||
if (stopFlag) return "stopped";
|
||||
|
||||
const btns = moreButtons();
|
||||
if (!btns.length) return "empty";
|
||||
|
||||
let chose = null;
|
||||
let classification = null;
|
||||
let skippedNow = 0;
|
||||
for (const btn of btns) {
|
||||
const c = classify(btn, skipTerms, offensiveTerms);
|
||||
if (c.action === "skip") {
|
||||
const lbl = btn.getAttribute("aria-label") || "";
|
||||
if (lbl && !countedSkipLabels.has(lbl)) {
|
||||
countedSkipLabels.add(lbl);
|
||||
state.skipped++;
|
||||
skippedNow++;
|
||||
} else if (!lbl) {
|
||||
skippedNow++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
chose = btn;
|
||||
classification = c;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!chose) {
|
||||
if (skippedNow) {
|
||||
log(`Skipped ${skippedNow} visible (Tesla/Ford/Pokémon/keep) — scrolling`);
|
||||
}
|
||||
window.scrollBy(0, 2200);
|
||||
await sleep(1100);
|
||||
const after = moreButtons();
|
||||
if (after.length === 0) return "exhausted-visible";
|
||||
return "scrolled";
|
||||
}
|
||||
|
||||
if (skippedNow) {
|
||||
log(`Passing ${skippedNow} skip-community item(s)`);
|
||||
}
|
||||
|
||||
const labelBefore = chose.getAttribute("aria-label") || "";
|
||||
const label = labelBefore.slice(0, 90);
|
||||
log(`Deleting: ${label}`);
|
||||
const ok = await openMenuAndAct(
|
||||
chose,
|
||||
state.mode === "posts"
|
||||
? ["move to trash", "delete", "trash"]
|
||||
: ["delete", "remove"]
|
||||
);
|
||||
|
||||
if (!ok || hasFbError()) {
|
||||
await backoffOnError(hasFbError() ? "Something went wrong" : "Delete failed");
|
||||
return "rate-limit";
|
||||
}
|
||||
|
||||
await waitForRowGone(chose, labelBefore);
|
||||
|
||||
if (hasFbError()) {
|
||||
await backoffOnError("Something went wrong");
|
||||
return "rate-limit";
|
||||
}
|
||||
|
||||
state.deleted++;
|
||||
state.failStreak = 0;
|
||||
if (classification.offensive) state.offensiveForced++;
|
||||
log(
|
||||
`${classification.offensive ? "Offensive " : ""}Deleted: ${label}`
|
||||
);
|
||||
await sleep(state.delayMs);
|
||||
return "deleted";
|
||||
}
|
||||
|
||||
async function runLoop() {
|
||||
const skipTerms = parseList(
|
||||
document.getElementById("fb-cleaner-skip")?.value || DEFAULT_SKIP
|
||||
);
|
||||
const offensiveTerms = parseList(
|
||||
document.getElementById("fb-cleaner-offensive")?.value || DEFAULT_OFFENSIVE
|
||||
);
|
||||
const delayRaw = Number(
|
||||
document.getElementById("fb-cleaner-delay")?.value || 2500
|
||||
);
|
||||
// Migrate old second-based values (< 100) to ms
|
||||
state.delayMs =
|
||||
delayRaw > 0 && delayRaw < 100
|
||||
? Math.round(delayRaw * 1000)
|
||||
: Math.max(0, Math.round(delayRaw || 2500));
|
||||
const delayInput = document.getElementById("fb-cleaner-delay");
|
||||
if (delayInput) delayInput.value = String(state.delayMs);
|
||||
state.failStreak = 0;
|
||||
countedSkipLabels.clear();
|
||||
|
||||
state.mode = detectMode();
|
||||
state.running = true;
|
||||
stopFlag = false;
|
||||
updateStats();
|
||||
setButtons(true);
|
||||
log(`Started in ${state.mode} mode · ${state.delayMs}ms between deletes`);
|
||||
|
||||
let emptyStreak = 0;
|
||||
while (!stopFlag) {
|
||||
if (!isActivityLog()) {
|
||||
log("Not on Activity Log — open Comments or Posts manage mode");
|
||||
break;
|
||||
}
|
||||
state.mode = detectMode();
|
||||
const result = await processOne(skipTerms, offensiveTerms);
|
||||
if (stopFlag) break;
|
||||
|
||||
if (result === "empty" || result === "exhausted-visible") {
|
||||
emptyStreak++;
|
||||
window.scrollBy(0, 2800);
|
||||
await sleep(1400);
|
||||
if (moreButtons().length === 0) {
|
||||
if (emptyStreak === 2) {
|
||||
window.scrollTo(0, 0);
|
||||
await sleep(1000);
|
||||
}
|
||||
}
|
||||
if (emptyStreak >= 6) {
|
||||
log("No more deletable items on this page");
|
||||
break;
|
||||
}
|
||||
} else if (result !== "rate-limit") {
|
||||
emptyStreak = 0;
|
||||
}
|
||||
}
|
||||
|
||||
state.running = false;
|
||||
setButtons(false);
|
||||
log("Stopped");
|
||||
updateStats();
|
||||
}
|
||||
|
||||
function setButtons(running) {
|
||||
const start = document.getElementById("fb-cleaner-start");
|
||||
const stop = document.getElementById("fb-cleaner-stop");
|
||||
if (start) start.disabled = running;
|
||||
if (stop) stop.disabled = !running;
|
||||
}
|
||||
|
||||
function ensurePanel() {
|
||||
if (document.getElementById("fb-cleaner-root")) return;
|
||||
|
||||
const root = document.createElement("div");
|
||||
root.id = "fb-cleaner-root";
|
||||
root.innerHTML = `
|
||||
<div id="fb-cleaner-panel">
|
||||
<div id="fb-cleaner-header">
|
||||
<strong>FB Activity Cleaner</strong>
|
||||
<button id="fb-cleaner-minimize" title="Collapse">–</button>
|
||||
</div>
|
||||
<div id="fb-cleaner-body">
|
||||
<label>
|
||||
<input type="checkbox" id="fb-cleaner-auto-mode" checked />
|
||||
Auto-detect Comments vs Posts page
|
||||
</label>
|
||||
<div>
|
||||
<div style="font-size:11px;color:#9ca3af;margin-bottom:4px">Skip communities (unless offensive)</div>
|
||||
<textarea id="fb-cleaner-skip">${DEFAULT_SKIP}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:11px;color:#9ca3af;margin-bottom:4px">Offensive phrases — always delete</div>
|
||||
<textarea id="fb-cleaner-offensive">${DEFAULT_OFFENSIVE}</textarea>
|
||||
</div>
|
||||
<label style="align-items:center">
|
||||
Delay until next delete (ms)
|
||||
<input type="number" id="fb-cleaner-delay" min="0" max="60000" step="50" value="2500" style="width:80px;margin-left:auto;border-radius:6px;border:1px solid #4b5563;background:#1f2937;color:#fff;padding:4px 6px" />
|
||||
</label>
|
||||
<div id="fb-cleaner-stats"></div>
|
||||
<div id="fb-cleaner-log"></div>
|
||||
<div id="fb-cleaner-actions">
|
||||
<button id="fb-cleaner-start" type="button">Start deletion</button>
|
||||
<button id="fb-cleaner-stop" type="button" disabled>Stop</button>
|
||||
</div>
|
||||
<div style="font-size:11px;color:#9ca3af;line-height:1.35">
|
||||
Keeps profile/cover/photo uploads on Posts. Skips Tesla/Ford/Pokémon unless offensive. Slows down automatically if Facebook rate-limits.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.documentElement.appendChild(root);
|
||||
|
||||
document.getElementById("fb-cleaner-start").onclick = () => {
|
||||
if (state.running) return;
|
||||
runLoop();
|
||||
};
|
||||
document.getElementById("fb-cleaner-stop").onclick = () => {
|
||||
stopFlag = true;
|
||||
log("Stop requested…");
|
||||
};
|
||||
document.getElementById("fb-cleaner-minimize").onclick = () => {
|
||||
root.classList.toggle("collapsed");
|
||||
};
|
||||
|
||||
// drag
|
||||
const header = document.getElementById("fb-cleaner-header");
|
||||
let dragging = false;
|
||||
let ox = 0;
|
||||
let oy = 0;
|
||||
header.addEventListener("mousedown", (e) => {
|
||||
dragging = true;
|
||||
const rect = root.getBoundingClientRect();
|
||||
ox = e.clientX - rect.left;
|
||||
oy = e.clientY - rect.top;
|
||||
e.preventDefault();
|
||||
});
|
||||
window.addEventListener("mousemove", (e) => {
|
||||
if (!dragging) return;
|
||||
root.style.left = Math.max(0, e.clientX - ox) + "px";
|
||||
root.style.top = Math.max(0, e.clientY - oy) + "px";
|
||||
root.style.right = "auto";
|
||||
root.style.bottom = "auto";
|
||||
});
|
||||
window.addEventListener("mouseup", () => {
|
||||
dragging = false;
|
||||
});
|
||||
|
||||
chrome.storage?.local?.get(["skip", "offensive", "delay"], (data) => {
|
||||
if (data?.skip) {
|
||||
document.getElementById("fb-cleaner-skip").value = data.skip;
|
||||
}
|
||||
if (data?.offensive) {
|
||||
document.getElementById("fb-cleaner-offensive").value = data.offensive;
|
||||
}
|
||||
if (data?.delay != null && data.delay !== "") {
|
||||
const n = Number(data.delay);
|
||||
const ms = n > 0 && n < 100 ? Math.round(n * 1000) : Math.round(n);
|
||||
document.getElementById("fb-cleaner-delay").value = String(
|
||||
Number.isFinite(ms) ? ms : 2500
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const persist = () => {
|
||||
chrome.storage?.local?.set({
|
||||
skip: document.getElementById("fb-cleaner-skip").value,
|
||||
offensive: document.getElementById("fb-cleaner-offensive").value,
|
||||
delay: document.getElementById("fb-cleaner-delay").value,
|
||||
});
|
||||
};
|
||||
document.getElementById("fb-cleaner-skip").addEventListener("change", persist);
|
||||
document
|
||||
.getElementById("fb-cleaner-offensive")
|
||||
.addEventListener("change", persist);
|
||||
document
|
||||
.getElementById("fb-cleaner-delay")
|
||||
.addEventListener("change", persist);
|
||||
|
||||
updateStats();
|
||||
log("Panel ready — click Start deletion");
|
||||
}
|
||||
|
||||
function startFromExternal() {
|
||||
ensurePanel();
|
||||
if (state.running) return { ok: true, alreadyRunning: true };
|
||||
runLoop();
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
function stopFromExternal() {
|
||||
stopFlag = true;
|
||||
log("Stop requested…");
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
|
||||
if (!msg || !msg.type) return false;
|
||||
if (msg.type === "FB_CLEANER_PING") {
|
||||
sendResponse({ ok: true });
|
||||
return false;
|
||||
}
|
||||
if (msg.type === "FB_CLEANER_START") {
|
||||
sendResponse(startFromExternal());
|
||||
return false;
|
||||
}
|
||||
if (msg.type === "FB_CLEANER_STOP") {
|
||||
sendResponse(stopFromExternal());
|
||||
return false;
|
||||
}
|
||||
if (msg.type === "FB_CLEANER_STATUS") {
|
||||
sendResponse({ ok: true, state: { ...state } });
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
function boot() {
|
||||
ensurePanel();
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", boot);
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user