From 3999bb29f9f6711df32aabd2428922c3d72045bf Mon Sep 17 00:00:00 2001
From: yoNico21 <119441054+yoNico21@users.noreply.github.com>
Date: Thu, 17 Oct 2024 14:25:16 +0200
Subject: [PATCH] working extension for chrome (Manifest V3) and Firefox
(Manifest V2) + created a package_me.sh for packaging the extensions for both
platforms
---
1000_bots_webextension/background.js | 128 ++++---
1000_bots_webextension/background_chrome.js | 312 ++++++++++++++++++
1000_bots_webextension/background_ff.js | 207 ++++++++++++
1000_bots_webextension/content.js | 196 ++++++-----
1000_bots_webextension/content_chrome.js | 132 ++++++++
1000_bots_webextension/content_ff.js | 144 ++++++++
1000_bots_webextension/manifest.json | 102 +++---
1000_bots_webextension/manifest_chrome.json | 56 ++++
1000_bots_webextension/manifest_ff.json | 32 ++
1000_bots_webextension/old.background.js | 79 -----
1000_bots_webextension/old.manifest.json | 22 --
1000_bots_webextension/package_me.sh | 70 ++++
.../refused_to_be_human-0.0.2.xpi | Bin 0 -> 10850 bytes
.../refused_to_be_human-0.0.2.zip | Bin 0 -> 12142 bytes
.../refused_to_be_human-0.0.3.xpi | Bin 0 -> 12032 bytes
.../refused_to_be_human-0.0.3.zip | Bin 0 -> 12718 bytes
16 files changed, 1205 insertions(+), 275 deletions(-)
create mode 100644 1000_bots_webextension/background_chrome.js
create mode 100644 1000_bots_webextension/background_ff.js
create mode 100644 1000_bots_webextension/content_chrome.js
create mode 100644 1000_bots_webextension/content_ff.js
create mode 100644 1000_bots_webextension/manifest_chrome.json
create mode 100644 1000_bots_webextension/manifest_ff.json
delete mode 100644 1000_bots_webextension/old.background.js
delete mode 100644 1000_bots_webextension/old.manifest.json
create mode 100755 1000_bots_webextension/package_me.sh
create mode 100644 1000_bots_webextension/packaged/refused_to_be_human-0.0.2/refused_to_be_human-0.0.2.xpi
create mode 100644 1000_bots_webextension/packaged/refused_to_be_human-0.0.2/refused_to_be_human-0.0.2.zip
create mode 100644 1000_bots_webextension/packaged/refused_to_be_human-0.0.3/refused_to_be_human-0.0.3.xpi
create mode 100644 1000_bots_webextension/packaged/refused_to_be_human-0.0.3/refused_to_be_human-0.0.3.zip
diff --git a/1000_bots_webextension/background.js b/1000_bots_webextension/background.js
index b0ecc22..286273c 100644
--- a/1000_bots_webextension/background.js
+++ b/1000_bots_webextension/background.js
@@ -20,22 +20,24 @@
"use strict";
+const DEBUG = false;
+
// Use chrome or browser depending on the environment
-var browser = chrome || browser;
+const browser = chrome || browser;
// Custom user-agent to spoof Googlebot
-var user_agent =
+const user_agent =
"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";
-var target_urls = "*://*/*";
+const target_urls = "*://*/*";
// Duration to display the injected HTML (in milliseconds)
const DISPLAY_DURATION = 8000; // Set to 8 seconds
// Initialize storage for duration
-browser.storage.local
+browser.storage.sync
.set({ duration: DISPLAY_DURATION })
.then(() => {
- console.log("Duration set to:", DISPLAY_DURATION);
+ if (DEBUG) console.log("Duration set to:", DISPLAY_DURATION);
})
.catch((error) => {
console.error("Error setting duration:", error);
@@ -43,14 +45,16 @@ browser.storage.local
// Set extension status to ON
function set_extension_status_ON() {
- browser.storage.local
- .set({ status: 1 })
+ browser.storage.sync
+ .set({ status: 1, bannerInjected: false, debug: DEBUG })
.then(() => {
- console.log("Extension status set to ON");
+ if (DEBUG) console.log("Extension status set to ON");
return browser.action.setIcon({ path: "icon_ON_48.png" });
})
.then(() => {
- return browser.action.setTitle({ title: "1000 Bots" });
+ return browser.action.setTitle({
+ title: "You are now surfing the web as a bot 🤖",
+ });
})
.then(() => {
// Start user-agent spoofing
@@ -71,14 +75,16 @@ function set_extension_status_ON() {
// Set extension status to OFF
function set_extension_status_OFF() {
- browser.storage.local
+ browser.storage.sync
.set({ status: 0 })
.then(() => {
- console.log("Extension status set to OFF");
+ if (DEBUG) console.log("Extension status set to OFF");
return browser.action.setIcon({ path: "icon_OFF_48.png" });
})
.then(() => {
- return browser.action.setTitle({ title: "1000 Bots activate" });
+ return browser.action.setTitle({
+ title: "Click here to refuse to be human 🤖",
+ });
})
.then(() => {
// Remove the content from all active tabs
@@ -99,7 +105,7 @@ function set_extension_status_OFF() {
// Toggle the icon between ON and OFF
function update_icon() {
- browser.storage.local
+ browser.storage.sync
.get("status")
.then((result) => {
if (result.status === 0) {
@@ -143,7 +149,10 @@ function startUserAgentSpoofing() {
removeRuleIds: [],
})
.then(() => {
- return console.log("User-Agent spoofing rules added successfully.");
+ if (DEBUG)
+ return console.log(
+ "User-Agent spoofing rules added successfully."
+ );
})
.then(() => {})
.catch((error) => {
@@ -158,11 +167,12 @@ function stopUserAgentSpoofing() {
removeRuleIds: [1],
})
.then(() => {
- console.log("User-Agent spoofing rules removed successfully.");
+ if (DEBUG)
+ console.log("User-Agent spoofing rules removed successfully.");
})
.then(() => {
// Clear the bannerInjected flag when stopping the extension
- return browser.storage.local.set({ bannerInjected: false });
+ return browser.storage.sync.set({ bannerInjected: false });
})
.catch((error) => {
console.error("Error removing rules:", error);
@@ -176,7 +186,7 @@ function refreshAllTabs() {
browser.tabs
.reload(tab.id)
.then(() => {
- console.log(`Refreshed tab: ${tab.id}`);
+ if (DEBUG) console.log(`Refreshed tab: ${tab.id}`);
})
.catch((error) => {
console.error(`Error refreshing tab ${tab.id}:`, error);
@@ -190,33 +200,57 @@ function injectContentScript() {
return browser.tabs
.query({})
.then((tabs) => {
- return Promise.all(
- tabs.map((tab) => {
- // Check if the tab is not a chrome URL
- if (!tab.url.startsWith("chrome://")) {
- return browser.scripting
- .executeScript({
- target: { tabId: tab.id },
- files: ["content.js"],
- })
- .then(() => {
+ // Create an array of promises for each tab
+ const promises = tabs.map((tab) => {
+ // Check if the URL is not something like chrome:// or about:// etc.
+ if (tab.url.startsWith("https://")) {
+ return browser.scripting
+ .executeScript({
+ target: { tabId: tab.id },
+ files: ["content.js"],
+ })
+ .then(() => {
+ if (DEBUG)
console.log(
`Content script injected into tab ${tab.id}`
);
- })
- .catch((error) => {
- console.error(
- `Error injecting content script into tab ${tab.id}:`,
- error
- );
- });
- } else {
+
+ // Use a hardcoded duration for removal
+ const duration = 80000; // 8 seconds or whatever you need
+
+ // Set a timeout to remove the content after the hardcoded duration
+ setTimeout(() => {
+ browser.storage.sync.set({
+ bannerInjected: true,
+ });
+ if (DEBUG)
+ console.log(
+ "Duration expired. Removing content."
+ );
+ removeInjectedContent(); // Ensure this is defined
+ }, duration);
+ })
+ .catch((error) => {
+ console.error(
+ `Error injecting content script into tab ${tab.id}:`,
+ error
+ );
+ });
+ } else {
+ if (DEBUG)
console.log(
- `Skipped injecting content script into tab ${tab.id} (chrome:// URL)`
+ `Skipped injecting content script into tab ${tab.id} (chrome://, about:// URL or similar)`
);
- }
- })
- );
+ return Promise.resolve(); // Return resolved promise for skipped tabs
+ }
+ });
+
+ // Return a promise that resolves when all injections are complete
+ return Promise.all(promises);
+ })
+ .then(() => {
+ if (DEBUG)
+ console.log("All scripts injected and removal timers set.");
})
.catch((error) => {
console.error("Error querying tabs:", error);
@@ -229,7 +263,7 @@ function removeInjectedContent() {
.query({})
.then((tabs) => {
tabs.forEach((tab) => {
- if (!tab.url.startsWith("chrome://")) {
+ if (tab.url.startsWith("https://")) {
browser.scripting
.executeScript({
target: { tabId: tab.id },
@@ -242,9 +276,10 @@ function removeInjectedContent() {
},
})
.then(() => {
- console.log(
- `Removed injected content from tab ${tab.id}`
- );
+ if (DEBUG)
+ console.log(
+ `Removed injected content from tab ${tab.id}`
+ );
})
.catch((error) => {
console.error(
@@ -253,9 +288,10 @@ function removeInjectedContent() {
);
});
} else {
- console.log(
- `Skipped removing injected content from tab ${tab.id} (chrome:// URL)`
- );
+ if (DEBUG)
+ console.log(
+ `Skipped removing injected content from tab ${tab.id} (chrome://, about:// URL or similar)`
+ );
}
});
})
diff --git a/1000_bots_webextension/background_chrome.js b/1000_bots_webextension/background_chrome.js
new file mode 100644
index 0000000..286273c
--- /dev/null
+++ b/1000_bots_webextension/background_chrome.js
@@ -0,0 +1,312 @@
+/*
+ 1000 Bots - Surf the Web as Googlebot
+
+ Copyleft (C) 2020 !Mediengruppe Bitnik, connect@bitnik.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+
+"use strict";
+
+const DEBUG = false;
+
+// Use chrome or browser depending on the environment
+const browser = chrome || browser;
+
+// Custom user-agent to spoof Googlebot
+const user_agent =
+ "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";
+const target_urls = "*://*/*";
+
+// Duration to display the injected HTML (in milliseconds)
+const DISPLAY_DURATION = 8000; // Set to 8 seconds
+
+// Initialize storage for duration
+browser.storage.sync
+ .set({ duration: DISPLAY_DURATION })
+ .then(() => {
+ if (DEBUG) console.log("Duration set to:", DISPLAY_DURATION);
+ })
+ .catch((error) => {
+ console.error("Error setting duration:", error);
+ });
+
+// Set extension status to ON
+function set_extension_status_ON() {
+ browser.storage.sync
+ .set({ status: 1, bannerInjected: false, debug: DEBUG })
+ .then(() => {
+ if (DEBUG) console.log("Extension status set to ON");
+ return browser.action.setIcon({ path: "icon_ON_48.png" });
+ })
+ .then(() => {
+ return browser.action.setTitle({
+ title: "You are now surfing the web as a bot 🤖",
+ });
+ })
+ .then(() => {
+ // Start user-agent spoofing
+ return startUserAgentSpoofing();
+ })
+ .then(() => {
+ // Refresh all tabs after starting user-agent spoofing
+ return refreshAllTabs();
+ })
+ .then(() => {
+ // Inject the content script into all active tabs after refreshing
+ return injectContentScript();
+ })
+ .catch((error) => {
+ console.error("Error setting extension status ON:", error);
+ });
+}
+
+// Set extension status to OFF
+function set_extension_status_OFF() {
+ browser.storage.sync
+ .set({ status: 0 })
+ .then(() => {
+ if (DEBUG) console.log("Extension status set to OFF");
+ return browser.action.setIcon({ path: "icon_OFF_48.png" });
+ })
+ .then(() => {
+ return browser.action.setTitle({
+ title: "Click here to refuse to be human 🤖",
+ });
+ })
+ .then(() => {
+ // Remove the content from all active tabs
+ return removeInjectedContent();
+ })
+ .then(() => {
+ // Stop user-agent spoofing
+ return stopUserAgentSpoofing();
+ })
+ .then(() => {
+ // Refresh all tabs before stopping the extension
+ return refreshAllTabs();
+ })
+ .catch((error) => {
+ console.error("Error setting extension status OFF:", error);
+ });
+}
+
+// Toggle the icon between ON and OFF
+function update_icon() {
+ browser.storage.sync
+ .get("status")
+ .then((result) => {
+ if (result.status === 0) {
+ set_extension_status_ON();
+ } else {
+ set_extension_status_OFF();
+ }
+ })
+ .catch((error) => {
+ console.error("Error fetching extension status:", error);
+ });
+}
+
+// Start user-agent spoofing
+function startUserAgentSpoofing() {
+ const rules = [
+ {
+ id: 1,
+ priority: 1,
+ action: {
+ type: "modifyHeaders",
+ requestHeaders: [
+ {
+ operation: "set",
+ header: "User-Agent",
+ value: user_agent,
+ },
+ ],
+ },
+ condition: {
+ urlFilter: target_urls,
+ resourceTypes: ["main_frame"],
+ },
+ },
+ ];
+
+ // Add the rules
+ return browser.declarativeNetRequest
+ .updateDynamicRules({
+ addRules: rules,
+ removeRuleIds: [],
+ })
+ .then(() => {
+ if (DEBUG)
+ return console.log(
+ "User-Agent spoofing rules added successfully."
+ );
+ })
+ .then(() => {})
+ .catch((error) => {
+ console.error("Error adding rules:", error);
+ });
+}
+
+// Stop user-agent spoofing
+function stopUserAgentSpoofing() {
+ return browser.declarativeNetRequest
+ .updateDynamicRules({
+ removeRuleIds: [1],
+ })
+ .then(() => {
+ if (DEBUG)
+ console.log("User-Agent spoofing rules removed successfully.");
+ })
+ .then(() => {
+ // Clear the bannerInjected flag when stopping the extension
+ return browser.storage.sync.set({ bannerInjected: false });
+ })
+ .catch((error) => {
+ console.error("Error removing rules:", error);
+ });
+}
+
+// Function to refresh all active tabs
+function refreshAllTabs() {
+ return browser.tabs.query({}).then((tabs) => {
+ tabs.forEach((tab) => {
+ browser.tabs
+ .reload(tab.id)
+ .then(() => {
+ if (DEBUG) console.log(`Refreshed tab: ${tab.id}`);
+ })
+ .catch((error) => {
+ console.error(`Error refreshing tab ${tab.id}:`, error);
+ });
+ });
+ });
+}
+
+// Inject the content.js script into all active tabs
+function injectContentScript() {
+ return browser.tabs
+ .query({})
+ .then((tabs) => {
+ // Create an array of promises for each tab
+ const promises = tabs.map((tab) => {
+ // Check if the URL is not something like chrome:// or about:// etc.
+ if (tab.url.startsWith("https://")) {
+ return browser.scripting
+ .executeScript({
+ target: { tabId: tab.id },
+ files: ["content.js"],
+ })
+ .then(() => {
+ if (DEBUG)
+ console.log(
+ `Content script injected into tab ${tab.id}`
+ );
+
+ // Use a hardcoded duration for removal
+ const duration = 80000; // 8 seconds or whatever you need
+
+ // Set a timeout to remove the content after the hardcoded duration
+ setTimeout(() => {
+ browser.storage.sync.set({
+ bannerInjected: true,
+ });
+ if (DEBUG)
+ console.log(
+ "Duration expired. Removing content."
+ );
+ removeInjectedContent(); // Ensure this is defined
+ }, duration);
+ })
+ .catch((error) => {
+ console.error(
+ `Error injecting content script into tab ${tab.id}:`,
+ error
+ );
+ });
+ } else {
+ if (DEBUG)
+ console.log(
+ `Skipped injecting content script into tab ${tab.id} (chrome://, about:// URL or similar)`
+ );
+ return Promise.resolve(); // Return resolved promise for skipped tabs
+ }
+ });
+
+ // Return a promise that resolves when all injections are complete
+ return Promise.all(promises);
+ })
+ .then(() => {
+ if (DEBUG)
+ console.log("All scripts injected and removal timers set.");
+ })
+ .catch((error) => {
+ console.error("Error querying tabs:", error);
+ });
+}
+
+// Remove the content from all active tabs
+function removeInjectedContent() {
+ return browser.tabs
+ .query({})
+ .then((tabs) => {
+ tabs.forEach((tab) => {
+ if (tab.url.startsWith("https://")) {
+ browser.scripting
+ .executeScript({
+ target: { tabId: tab.id },
+ function: () => {
+ const banner =
+ document.querySelector(".extension-banner");
+ if (banner && banner.parentNode) {
+ banner.parentNode.removeChild(banner);
+ }
+ },
+ })
+ .then(() => {
+ if (DEBUG)
+ console.log(
+ `Removed injected content from tab ${tab.id}`
+ );
+ })
+ .catch((error) => {
+ console.error(
+ `Error removing injected content from tab ${tab.id}:`,
+ error
+ );
+ });
+ } else {
+ if (DEBUG)
+ console.log(
+ `Skipped removing injected content from tab ${tab.id} (chrome://, about:// URL or similar)`
+ );
+ }
+ });
+ })
+ .catch((error) => {
+ console.error(
+ "Error querying tabs for injected content removal:",
+ error
+ );
+ });
+}
+
+// Event listeners for Manifest v3 Service Worker
+browser.runtime.onInstalled.addListener(() => {
+ set_extension_status_OFF(); // Set status to OFF on installation/startup
+});
+
+// Handle browser action clicks to toggle status
+browser.action.onClicked.addListener(update_icon);
diff --git a/1000_bots_webextension/background_ff.js b/1000_bots_webextension/background_ff.js
new file mode 100644
index 0000000..3680bca
--- /dev/null
+++ b/1000_bots_webextension/background_ff.js
@@ -0,0 +1,207 @@
+/*
+ 1000 Bots - Surf the Web as Googlebot
+
+ Copyleft (C) 2020 !Mediengruppe Bitnik, connect@bitnik.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+
+"use strict";
+
+var browser = browser || chrome;
+var user_agent =
+ "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";
+var target_urls = "*://*/*";
+var status = 0;
+
+const DEBUG = false; // Set to true to enable debug mode
+
+// Duration to display the injected HTML (in milliseconds)
+const DISPLAY_DURATION = 8000; // Set to 8 seconds
+
+// Initialize storage for duration
+browser.storage.sync.set(
+ { duration: DISPLAY_DURATION, debug: DEBUG },
+ function () {
+ if (browser.runtime.lastError) {
+ console.error("Error setting duration:", browser.runtime.lastError);
+ } else {
+ if (DEBUG) console.log("Duration set to:", DISPLAY_DURATION);
+ }
+ }
+);
+
+// Inject the content.js script into all active tabs
+function injectContentScript() {
+ browser.tabs.query({}, (tabs) => {
+ tabs.forEach((tab) => {
+ // Check if the URL is not something like chrome:// or about:// etc.
+ if (tab.url.startsWith("https://")) {
+ browser.tabs.executeScript(
+ tab.id,
+ { file: "content.js" },
+ () => {
+ if (browser.runtime.lastError) {
+ console.error(
+ `Error injecting content script into tab ${tab.id}:`,
+ browser.runtime.lastError
+ );
+ } else {
+ if (DEBUG)
+ console.log(
+ `Content script injected into tab ${tab.id}`
+ );
+
+ // If the duration has passed, removeInjectedContent
+ const interval = setInterval(() => {
+ browser.storage.sync.get(
+ "duration",
+ (result) => {
+ if (browser.runtime.lastError) {
+ console.error(
+ "Error fetching duration:",
+ browser.runtime.lastError
+ );
+ } else if (result.duration <= 0) {
+ clearInterval(interval);
+ removeInjectedContent();
+ }
+ }
+ );
+ });
+ }
+ }
+ );
+ } else {
+ if (DEBUG)
+ console.log(
+ `Skipped injecting content script into tab ${tab.id} (chrome://, about:// URL or similar)`
+ );
+ }
+ });
+ });
+}
+
+// Remove the content from all active tabs
+function removeInjectedContent() {
+ browser.tabs.query({}, (tabs) => {
+ tabs.forEach((tab) => {
+ if (tab.url.startsWith("https://")) {
+ browser.tabs.executeScript(
+ tab.id,
+ {
+ code: `
+ const banner = document.querySelector('.extension-banner');
+ if (banner && banner.parentNode) {
+ banner.parentNode.removeChild(banner);
+ }
+ `,
+ },
+ () => {
+ if (browser.runtime.lastError) {
+ console.error(
+ `Error removing injected content from tab ${tab.id}:`,
+ browser.runtime.lastError
+ );
+ } else {
+ if (DEBUG)
+ console.log(
+ `Removed injected content from tab ${tab.id}`
+ );
+ }
+ }
+ );
+ } else {
+ if (DEBUG)
+ console.log(
+ `Skipped removing injected content from tab ${tab.id} (chrome://, about:// URL or similar)`
+ );
+ }
+ });
+ });
+}
+
+// Function to refresh all active tabs
+function refreshAllTabs() {
+ browser.tabs.query({}, (tabs) => {
+ tabs.forEach((tab) => {
+ browser.tabs.reload(tab.id, () => {
+ if (browser.runtime.lastError) {
+ console.error(
+ `Error refreshing tab ${tab.id}:`,
+ browser.runtime.lastError
+ );
+ } else {
+ if (DEBUG) console.log(`Refreshed tab: ${tab.id}`);
+ }
+ });
+ });
+ });
+}
+
+function set_extension_status_ON() {
+ status = 1;
+ if (DEBUG) console.log("Setting status to ON");
+ browser.browserAction.setIcon({ path: "icon_ON_48.png" });
+ browser.browserAction.setTitle({
+ title: "You are now surfing the web as a bot 🤖",
+ });
+ injectContentScript();
+}
+
+function set_extension_status_OFF() {
+ status = 0;
+ if (DEBUG) console.log("Setting status to OFF");
+ browser.browserAction.setIcon({ path: "icon_OFF_48.png" });
+ browser.browserAction.setTitle({
+ title: "Click here to refuse to be human 🤖",
+ });
+ removeInjectedContent();
+ refreshAllTabs();
+}
+
+function update_icon() {
+ if (status == 0) {
+ set_extension_status_ON();
+ } else {
+ set_extension_status_OFF();
+ }
+}
+
+function rewrite_user_agent_header(e) {
+ if (DEBUG) console.log("Browser http request!");
+
+ if (status == 0) {
+ if (DEBUG) console.log("Nothing to do");
+ return { requestHeaders: e.requestHeaders };
+ }
+
+ for (var header of e.requestHeaders) {
+ if (header.name.toLowerCase() === "user-agent") {
+ header.value = user_agent;
+ }
+ }
+ if (DEBUG) console.log("Set user-agent header to: " + user_agent);
+ return { requestHeaders: e.requestHeaders };
+}
+
+browser.runtime.onStartup.addListener(set_extension_status_OFF);
+browser.browserAction.onClicked.addListener(update_icon);
+
+browser.webRequest.onBeforeSendHeaders.addListener(
+ rewrite_user_agent_header,
+ { urls: [target_urls] },
+ ["blocking", "requestHeaders"]
+);
diff --git a/1000_bots_webextension/content.js b/1000_bots_webextension/content.js
index 0e72239..f9c47f9 100644
--- a/1000_bots_webextension/content.js
+++ b/1000_bots_webextension/content.js
@@ -1,94 +1,130 @@
+// Use chrome or browser depending on the environment
+const browser = chrome || browser;
+
+let DEBUG; // Declare the DEBUG variable in the outer scope
+
+browser.storage.sync.get("debug").then((result) => {
+ DEBUG = result.debug;
+ console.log("DEBUG mode:", DEBUG);
+});
+
+// Duration to show the banner (in milliseconds)
+let DISPLAY_DURATION = 5000; // Initialize the duration to 5 seconds
+let CSS_ANIMATION_DURATION,
+ CSS_DURATION,
+ CSS_DELAY = 0; // Initialize the CSS variables
+let BANNER; // Declare the banner variable in the outer scope
+
+function initializeCSSProperties(result) {
+ DISPLAY_DURATION = result.duration || DISPLAY_DURATION;
+ if (DEBUG) console.log("Duration set to:", DISPLAY_DURATION, "(in ms)");
+
+ // Set variables to css for delay for animation
+ CSS_ANIMATION_DURATION = 300 / 1000;
+ CSS_DURATION = DISPLAY_DURATION / 1000;
+ CSS_DELAY = CSS_DURATION - CSS_ANIMATION_DURATION - CSS_ANIMATION_DURATION;
+
+ if (DEBUG) console.log("--duration:", `${CSS_ANIMATION_DURATION}s`);
+ if (DEBUG) console.log("--delay:", `${CSS_DURATION}s`);
+
+ CSS_ANIMATION_DURATION = CSS_ANIMATION_DURATION + "s";
+ document.body.style.setProperty("--duration", CSS_ANIMATION_DURATION);
+
+ CSS_DELAY = CSS_DELAY + "s";
+ document.body.style.setProperty("--delay", CSS_DELAY);
+}
+
+function injectCSS() {
+ // Inject the CSS file
+ const link = document.createElement("link");
+ link.href = browser.runtime.getURL("style.css");
+ link.rel = "stylesheet";
+ document.head.appendChild(link);
+
+ // Return the link element
+ return link;
+}
+
+function createBanner() {
+ // Create the banner container
+ const banner = document.createElement("div");
+ banner.classList.add("extension-banner-container");
+
+ const marqeeText = "YOU ARE NOW SURFING THE WEB AS A BOT 🤖 ";
+
+ const bannerContent = `
+
+
+
+ ${new Array(30).fill(marqeeText).join("")}
+
+
+
+ `;
+
+ // Set the banner content
+ banner.innerHTML = bannerContent;
+
+ // Append the banner to the body
+ document.body.appendChild(banner);
+
+ // Return the banner
+ return banner;
+}
+
+function removeBanner() {
+ setTimeout(() => {
+ if (BANNER && BANNER.parentNode) {
+ // Remove the banner
+ BANNER.parentNode.removeChild(BANNER);
+ if (DEBUG) console.log("Banner removed after duration.");
+
+ // Remove the injected CSS file
+ const link = document.querySelector('link[href$="style.css"]'); // Match any href ending with "style.css"
+ link.parentNode.removeChild(link);
+ }
+ }, DISPLAY_DURATION);
+}
+
(function () {
- // Use chrome or browser depending on the environment
- var browser = chrome || browser;
-
- // Duration to show the banner (in milliseconds)
- let DISPLAY_DURATION = 5000; // Initialize the duration to 5 seconds
- let banner; // Declare the banner variable in the outer scope
-
// Get the duration from local storage and update the DISPLAY_DURATION variable
- browser.storage.local
- .get(["duration", "status"]) // Fetch both duration and status
+ browser.storage.sync
+ .get(["duration", "status", "bannerInjected"]) // Fetch both duration and status
.then((result) => {
- DISPLAY_DURATION = result.duration || DISPLAY_DURATION;
- console.log("Duration set to:", DISPLAY_DURATION, "(in ms)");
+ if (DEBUG)
+ console.log(
+ "content.js reads from storage: bannerInjected =",
+ result.bannerInjected
+ );
+ if (!result.bannerInjected) {
+ // Initialize the CSS properties
+ initializeCSSProperties(result);
- // Set variables to css for delay for animation
- let CSS_ANIMATION_DURATION = 300 / 1000;
- let CSS_DURATION = DISPLAY_DURATION / 1000;
- let CSS_DELAY =
- CSS_DURATION - CSS_ANIMATION_DURATION - CSS_ANIMATION_DURATION;
+ if (result.status === 0) {
+ // Extension is OFF, do nothing
+ if (DEBUG) console.log("Extension is OFF, exiting.");
+ return;
+ }
- console.log("--duration:", `${CSS_ANIMATION_DURATION}s`);
- console.log("--delay:", `${CSS_DURATION}s`);
+ if (DEBUG) console.log("Injecting CSS...");
- CSS_ANIMATION_DURATION = CSS_ANIMATION_DURATION + "s";
- document.body.style.setProperty(
- "--duration",
- CSS_ANIMATION_DURATION
- );
+ // Inject the CSS file
+ injectCSS();
+ if (DEBUG) console.log("CSS file injected.");
- CSS_DELAY = CSS_DELAY + "s";
- document.body.style.setProperty("--delay", CSS_DELAY);
-
- if (result.status === 0) {
- // Extension is OFF, do nothing
- console.log("Extension is OFF, exiting.");
- return;
+ // Create the banner
+ BANNER = createBanner();
+ if (DEBUG) console.log("Banner injected:", BANNER);
+ } else {
+ // Banner has already been injected, remove the banner
+ if (DEBUG)
+ console.log("Banner already injected... removing banner.");
+ removeBanner();
}
-
- if (result.bannerInjected) {
- // Banner has already been injected, do nothing
- console.log("Banner already injected, exiting.");
- return;
- }
-
- console.log("Injecting the banner...");
-
- // Inject the CSS file
- const link = document.createElement("link");
- link.href = browser.runtime.getURL("style.css");
- link.rel = "stylesheet";
- document.head.appendChild(link);
- console.log("CSS file injected.");
-
- // Create and style the banner container
- banner = document.createElement("div");
- banner.classList.add("extension-banner-container");
-
- const marqeeText = "YOU ARE NOW SURFING THE WEB AS A BOT 🤖 ";
-
- const bannerContent = `
-
-
-
- ${new Array(30).fill(marqeeText).join("")}
-
-
-
- `;
-
- // Set the banner content
- banner.innerHTML = bannerContent;
-
- // Append the banner to the body
- document.body.appendChild(banner);
- console.log("Banner injected:", banner);
-
- // Set the flag in local storage to indicate that the banner was injected
- return browser.storage.local.set({ bannerInjected: true });
})
.then(() => {
// Remove the banner after the specified duration
- setTimeout(() => {
- if (banner && banner.parentNode) {
- banner.parentNode.removeChild(banner);
- console.log("Banner removed after duration.");
-
- // Reset the flag when the banner is removed
- browser.storage.local.set({ bannerInjected: false });
- }
- }, DISPLAY_DURATION);
+ removeBanner();
})
.catch((error) => {
console.error("Error in content script:", error);
diff --git a/1000_bots_webextension/content_chrome.js b/1000_bots_webextension/content_chrome.js
new file mode 100644
index 0000000..b662b75
--- /dev/null
+++ b/1000_bots_webextension/content_chrome.js
@@ -0,0 +1,132 @@
+// Use chrome or browser depending on the environment
+const browser = chrome || browser;
+
+let DEBUG; // Declare the DEBUG variable in the outer scope
+
+browser.storage.sync.get("debug").then((result) => {
+ DEBUG = result.debug;
+ if (DEBUG) console.log("DEBUG mode:", DEBUG);
+});
+
+// Duration to show the banner (in milliseconds)
+let DISPLAY_DURATION = 5000; // Initialize the duration to 5 seconds
+let CSS_ANIMATION_DURATION,
+ CSS_DURATION,
+ CSS_DELAY = 0; // Initialize the CSS variables
+let BANNER; // Declare the banner variable in the outer scope
+
+function initializeCSSProperties(result) {
+ DISPLAY_DURATION = result.duration || DISPLAY_DURATION;
+ if (DEBUG) console.log("Duration set to:", DISPLAY_DURATION, "(in ms)");
+
+ // Set variables to css for delay for animation
+ CSS_ANIMATION_DURATION = 300 / 1000;
+ CSS_DURATION = DISPLAY_DURATION / 1000;
+ CSS_DELAY = CSS_DURATION - CSS_ANIMATION_DURATION - CSS_ANIMATION_DURATION;
+
+ if (DEBUG) console.log("--duration:", `${CSS_ANIMATION_DURATION}s`);
+ if (DEBUG) console.log("--delay:", `${CSS_DURATION}s`);
+
+ CSS_ANIMATION_DURATION = CSS_ANIMATION_DURATION + "s";
+ document.body.style.setProperty("--duration", CSS_ANIMATION_DURATION);
+
+ CSS_DELAY = CSS_DELAY + "s";
+ document.body.style.setProperty("--delay", CSS_DELAY);
+}
+
+function injectCSS() {
+ // Inject the CSS file
+ const link = document.createElement("link");
+ link.href = browser.runtime.getURL("style.css");
+ link.rel = "stylesheet";
+ document.head.appendChild(link);
+
+ // Return the link element
+ return link;
+}
+
+function createBanner() {
+ // Create the banner container
+ const banner = document.createElement("div");
+ banner.classList.add("extension-banner-container");
+
+ const marqeeText = "YOU ARE NOW SURFING THE WEB AS A BOT 🤖 ";
+
+ const bannerContent = `
+
+
+
+ ${new Array(30).fill(marqeeText).join("")}
+
+
+
+ `;
+
+ // Set the banner content
+ banner.innerHTML = bannerContent;
+
+ // Append the banner to the body
+ document.body.appendChild(banner);
+
+ // Return the banner
+ return banner;
+}
+
+function removeBanner() {
+ setTimeout(() => {
+ if (BANNER && BANNER.parentNode) {
+ // Remove the banner
+ BANNER.parentNode.removeChild(BANNER);
+ if (DEBUG) console.log("Banner removed after duration.");
+
+ // Remove the injected CSS file
+ const link = document.querySelector('link[href$="style.css"]'); // Match any href ending with "style.css"
+ link.parentNode.removeChild(link);
+ }
+ }, DISPLAY_DURATION);
+}
+
+(function () {
+ // Get the duration from local storage and update the DISPLAY_DURATION variable
+ browser.storage.sync
+ .get(["duration", "status", "bannerInjected"]) // Fetch both duration and status
+ .then((result) => {
+ if (DEBUG)
+ console.log(
+ "content.js reads from storage: bannerInjected =",
+ result.bannerInjected
+ );
+ if (!result.bannerInjected) {
+ // Initialize the CSS properties
+ initializeCSSProperties(result);
+
+ if (result.status === 0) {
+ // Extension is OFF, do nothing
+ if (DEBUG) console.log("Extension is OFF, exiting.");
+ return;
+ }
+
+ if (DEBUG) console.log("Injecting CSS...");
+
+ // Inject the CSS file
+ injectCSS();
+ if (DEBUG) console.log("CSS file injected.");
+
+ // Create the banner
+ BANNER = createBanner();
+ if (DEBUG) console.log("Banner injected:", BANNER);
+ } else {
+ // Banner has already been injected, remove the banner
+ if (DEBUG)
+ console.log("Banner already injected... removing banner.");
+ removeBanner();
+ }
+ })
+ .then(() => {
+ // Remove the banner after the specified duration
+ removeBanner();
+ })
+ .catch((error) => {
+ console.error("Error in content script:", error);
+ });
+})();
diff --git a/1000_bots_webextension/content_ff.js b/1000_bots_webextension/content_ff.js
new file mode 100644
index 0000000..98a13e7
--- /dev/null
+++ b/1000_bots_webextension/content_ff.js
@@ -0,0 +1,144 @@
+// Use chrome or browser depending on the environment
+const browser = chrome || browser;
+
+let DEBUG; // Declare the DEBUG variable in the outer scope
+
+browser.storage.sync.get("debug").then((result) => {
+ DEBUG = result.debug;
+ if (DEBUG) console.log("DEBUG mode:", DEBUG);
+});
+
+// Duration to show the banner (in milliseconds)
+let DISPLAY_DURATION = 5000; // Initialize the duration to 5 seconds
+let CSS_ANIMATION_DURATION,
+ CSS_DURATION,
+ CSS_DELAY = 0; // Initialize the CSS variables
+let BANNER; // Declare the banner variable in the outer scope
+
+function initializeCSSProperties(result) {
+ DISPLAY_DURATION = result.duration || DISPLAY_DURATION;
+ if (DEBUG) console.log("Duration set to:", DISPLAY_DURATION, "(in ms)");
+
+ // Set variables to css for delay for animation
+ CSS_ANIMATION_DURATION = 300 / 1000;
+ CSS_DURATION = DISPLAY_DURATION / 1000;
+ CSS_DELAY = CSS_DURATION - CSS_ANIMATION_DURATION - CSS_ANIMATION_DURATION;
+
+ if (DEBUG) console.log("--duration:", `${CSS_ANIMATION_DURATION}s`);
+ if (DEBUG) console.log("--delay:", `${CSS_DURATION}s`);
+
+ CSS_ANIMATION_DURATION = CSS_ANIMATION_DURATION + "s";
+ document.body.style.setProperty("--duration", CSS_ANIMATION_DURATION);
+
+ CSS_DELAY = CSS_DELAY + "s";
+ document.body.style.setProperty("--delay", CSS_DELAY);
+}
+
+function injectCSS() {
+ // Inject the CSS file
+ const link = document.createElement("link");
+ link.href = browser.runtime.getURL("style.css");
+ link.rel = "stylesheet";
+ document.head.appendChild(link);
+
+ // Return the link element
+ return link;
+}
+
+function createBanner() {
+ // Create the banner container
+ const banner = document.createElement("div");
+ banner.classList.add("extension-banner-container");
+
+ const marqeeText = "YOU ARE NOW SURFING THE WEB AS A BOT 🤖 ";
+
+ const bannerContent = `
+
+
+
+ ${new Array(30).fill(marqeeText).join("")}
+
+
+
+ `;
+
+ // Set the banner content
+ banner.innerHTML = bannerContent;
+
+ // Append the banner to the body
+ document.body.appendChild(banner);
+
+ // Return the banner
+ return banner;
+}
+
+(function () {
+ // Get the duration from local storage and update the DISPLAY_DURATION variable
+ browser.storage.sync.get(["duration", "status"], function (result) {
+ // Initialize the CSS properties
+ initializeCSSProperties(result);
+
+ if (result.status === 0) {
+ // Extension is OFF, do nothing
+ if (DEBUG) console.log("Extension is OFF, exiting.");
+ return;
+ }
+
+ if (result.bannerInjected === true) {
+ // Banner has already been injected, do nothing
+ if (DEBUG) console.log("Banner already injected, exiting.");
+ return;
+ }
+
+ if (DEBUG) console.log("Injecting CSS...");
+
+ // Inject the CSS file
+ injectCSS();
+ if (DEBUG) console.log("CSS file injected.");
+
+ // Create the banner
+ BANNER = createBanner();
+ if (DEBUG) console.log("Banner injected:", BANNER);
+
+ // Set the flag in local storage to indicate that the banner was injected
+ browser.storage.sync.set({ bannerInjected: true }, function () {
+ if (browser.runtime.lastError) {
+ console.error(
+ "Error setting bannerInjected flag:",
+ browser.runtime.lastError
+ );
+ } else {
+ // Remove the banner after the specified duration
+ setTimeout(() => {
+ if (BANNER && BANNER.parentNode) {
+ // Remove the banner
+ BANNER.parentNode.removeChild(BANNER);
+ if (DEBUG)
+ console.log("Banner removed after duration.");
+
+ // Remove the injected CSS file
+ const link = document.querySelector(
+ 'link[href="style.css"]'
+ );
+ if (link) {
+ link.parentNode.removeChild(link); // Corrected to remove the link
+ }
+
+ // Reset the flag when the banner is removed
+ browser.storage.sync.set(
+ { bannerInjected: false },
+ function () {
+ if (browser.runtime.lastError) {
+ console.error(
+ "Error resetting bannerInjected flag:",
+ browser.runtime.lastError
+ );
+ }
+ }
+ );
+ }
+ }, DISPLAY_DURATION);
+ }
+ });
+ });
+})();
diff --git a/1000_bots_webextension/manifest.json b/1000_bots_webextension/manifest.json
index 6831b9a..46aeacc 100644
--- a/1000_bots_webextension/manifest.json
+++ b/1000_bots_webextension/manifest.json
@@ -1,50 +1,56 @@
{
- "name": "1000 Bots",
- "version": "0.0.1",
- "description": "Surf the Web as Googlebot. Ever wondered what the Googlebot get’s to see online that you do not?",
- "homepage_url": "http://1000scores.com/portfolio-items/mediengruppe-bitnik-1000-bots/",
- "manifest_version": 3,
-
- "background": {
- "service_worker": "background.js"
- },
-
- "permissions": [
- "storage",
- "tabs",
- "scripting",
- "activeTab",
- "declarativeNetRequest",
- "declarativeNetRequestFeedback"
- ],
-
- "host_permissions": ["*://*/*"],
-
- "action": {
- "default_title": "Click to change your browser's perspective",
- "default_icon": "icon_OFF_48.png"
- },
-
- "content_scripts": [
- {
- "matches": [""],
- "js": ["content.js"],
- "css": ["style.css"],
- "run_at": "document_idle"
- }
- ],
-
- "web_accessible_resources": [
- {
- "resources": ["style.css"],
- "matches": [""]
- }
- ],
-
- "browser_specific_settings": {
- "gecko": {
- "id": "trash@bitnik.org",
- "strict_min_version": "55.0"
- }
- }
+ "name": "Refuse to be Human — Surf the Web as Googlebot",
+ "short_name": "Refuse to be Human",
+ "version": "0.0.3",
+ "description": "Surf the Web as Googlebot. Ever wondered what the Googlebot get’s to see online that you do not?",
+ "homepage_url": "http://1000scores.com/portfolio-items/mediengruppe-bitnik-1000-bots/",
+ "manifest_version": 3,
+ "background": {
+ "service_worker": "background.js"
+ },
+ "permissions": [
+ "storage",
+ "tabs",
+ "scripting",
+ "activeTab",
+ "declarativeNetRequest",
+ "declarativeNetRequestFeedback"
+ ],
+ "host_permissions": [
+ "*://*/*"
+ ],
+ "action": {
+ "default_title": "Click to change your browser's perspective",
+ "default_icon": "icon_OFF_48.png"
+ },
+ "content_scripts": [
+ {
+ "matches": [
+ ""
+ ],
+ "js": [
+ "content.js"
+ ],
+ "css": [
+ "style.css"
+ ],
+ "run_at": "document_idle"
+ }
+ ],
+ "web_accessible_resources": [
+ {
+ "resources": [
+ "style.css"
+ ],
+ "matches": [
+ ""
+ ]
+ }
+ ],
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "trash@bitnik.org",
+ "strict_min_version": "55.0"
+ }
+ }
}
diff --git a/1000_bots_webextension/manifest_chrome.json b/1000_bots_webextension/manifest_chrome.json
new file mode 100644
index 0000000..46aeacc
--- /dev/null
+++ b/1000_bots_webextension/manifest_chrome.json
@@ -0,0 +1,56 @@
+{
+ "name": "Refuse to be Human — Surf the Web as Googlebot",
+ "short_name": "Refuse to be Human",
+ "version": "0.0.3",
+ "description": "Surf the Web as Googlebot. Ever wondered what the Googlebot get’s to see online that you do not?",
+ "homepage_url": "http://1000scores.com/portfolio-items/mediengruppe-bitnik-1000-bots/",
+ "manifest_version": 3,
+ "background": {
+ "service_worker": "background.js"
+ },
+ "permissions": [
+ "storage",
+ "tabs",
+ "scripting",
+ "activeTab",
+ "declarativeNetRequest",
+ "declarativeNetRequestFeedback"
+ ],
+ "host_permissions": [
+ "*://*/*"
+ ],
+ "action": {
+ "default_title": "Click to change your browser's perspective",
+ "default_icon": "icon_OFF_48.png"
+ },
+ "content_scripts": [
+ {
+ "matches": [
+ ""
+ ],
+ "js": [
+ "content.js"
+ ],
+ "css": [
+ "style.css"
+ ],
+ "run_at": "document_idle"
+ }
+ ],
+ "web_accessible_resources": [
+ {
+ "resources": [
+ "style.css"
+ ],
+ "matches": [
+ ""
+ ]
+ }
+ ],
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "trash@bitnik.org",
+ "strict_min_version": "55.0"
+ }
+ }
+}
diff --git a/1000_bots_webextension/manifest_ff.json b/1000_bots_webextension/manifest_ff.json
new file mode 100644
index 0000000..fa79983
--- /dev/null
+++ b/1000_bots_webextension/manifest_ff.json
@@ -0,0 +1,32 @@
+{
+ "homepage_url": "http://1000scores.com/portfolio-items/mediengruppe-bitnik-1000-bots/",
+ "name": "Refuse to be Human — Surf the Web as Googlebot",
+ "short_name": "Refuse to be Human",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "trash@bitnik.org",
+ "strict_min_version": "55.0"
+ }
+ },
+ "description": "Surf the Web as Googlebot. Ever wondered what the Googlebot get’s to see online that you do not?",
+ "version": "0.0.3",
+ "background": {
+ "scripts": [
+ "background.js"
+ ],
+ "persistent": true
+ },
+ "permissions": [
+ "tabs",
+ "activeTab",
+ "webRequest",
+ "webRequestBlocking",
+ "storage",
+ "*://*/*"
+ ],
+ "browser_action": {
+ "name": "Click to change your browsers perspective",
+ "default_icon": "icon_OFF_48.png"
+ },
+ "manifest_version": 2
+}
diff --git a/1000_bots_webextension/old.background.js b/1000_bots_webextension/old.background.js
deleted file mode 100644
index 34f592f..0000000
--- a/1000_bots_webextension/old.background.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- 1000 Bots - Surf the Web as Googlebot
-
- Copyleft (C) 2020 !Mediengruppe Bitnik, connect@bitnik.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-*/
-
-'use strict';
-
-var browser = browser || chrome
-var user_agent = "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";
-var target_urls = "*://*/*";
-var status = 0;
-
-
-function set_extension_status_ON() {
- status = 1;
- //console.log('Setting status to ON');
- browser.browserAction.setIcon({path: 'icon_ON_48.png'});
- browser.browserAction.setTitle({title: "1000 Bots"});
-}
-
-function set_extension_status_OFF() {
- status = 0;
- //console.log('Setting status to OFF');
- browser.browserAction.setIcon({path: 'icon_OFF_48.png'});
- browser.browserAction.setTitle({title: "1000 Bots activate"});
-}
-
-function update_icon(){
- if(status == 0) {
- set_extension_status_ON();
- }else{
- set_extension_status_OFF();
- }
-}
-
-
-function rewrite_user_agent_header(e){
-
- //console.log('Browser http request!');
-
- if(status == 0) {
- // console.log('Nothing to do');
- return {requestHeaders: e.requestHeaders};
- }
-
- for (var header of e.requestHeaders) {
- if (header.name.toLowerCase() === "user-agent") {
- header.value = user_agent;
- }
- }
- //console.log('Set user-agent header to: ' + user_agent);
- return {requestHeaders: e.requestHeaders};
-
-}
-
-
-browser.runtime.onStartup.addListener(set_extension_status_OFF);
-browser.browserAction.onClicked.addListener(update_icon);
-
-browser.webRequest.onBeforeSendHeaders.addListener(
- rewrite_user_agent_header,
- {urls: [target_urls]},
- ["blocking", "requestHeaders"]
-);
diff --git a/1000_bots_webextension/old.manifest.json b/1000_bots_webextension/old.manifest.json
deleted file mode 100644
index e5ff046..0000000
--- a/1000_bots_webextension/old.manifest.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "homepage_url": "http://1000scores.com/portfolio-items/mediengruppe-bitnik-1000-bots/",
- "name": "1000 Bots",
- "browser_specific_settings": {
- "gecko": {
- "id": "trash@bitnik.org",
- "strict_min_version": "55.0"
- }
- },
- "description": "Surf the Web as Googlebot. Ever wondered what the Googlebot get’s to see online that you do not?",
- "version": "0.0.1",
- "background": {
- "scripts": ["background.js"],
- "persistent": true
- },
- "permissions": ["webRequest", "webRequestBlocking", "*://*/*"],
- "browser_action": {
- "name": "Click to change your browsers perspective",
- "default_icon": "icon_OFF_48.png"
- },
- "manifest_version": 2
-}
diff --git a/1000_bots_webextension/package_me.sh b/1000_bots_webextension/package_me.sh
new file mode 100755
index 0000000..cab6b78
--- /dev/null
+++ b/1000_bots_webextension/package_me.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+# Export folder
+export_folder="packaged"
+
+# Name of the extension
+extension_name="refused_to_be_human"
+
+# Files to include in the package
+include_files="background.js content.js icon_OFF_48.png icon_ON_48.png manifest.json style.css"
+
+# Get the current version using jq to parse JSON (if jq is available)
+current_version=$(jq -r '.version' manifest.json)
+
+# If jq is not available, fallback to a simpler grep (removing the -P flag)
+# current_version=$(grep -o '"version": "[0-9.]*"' manifest_ff.json | grep -o '[0-9.]*')
+
+# Split version into major, minor, and patch
+IFS='.' read -r major minor patch <<< "$current_version"
+
+# Increment the patch version by 1
+new_patch=$((patch + 1))
+
+# Format the new version
+new_version="$major.$minor.$new_patch"
+
+# Output the new version
+echo "New version: $new_version"
+
+# Update version in manifest.json, manifest_ff.json, and manifest_chrome.json
+for file in manifest.json manifest_ff.json manifest_chrome.json; do
+ if [ -f "$file" ]; then
+ # Use jq to update the version in each JSON file
+ jq --arg new_version "$new_version" '.version = $new_version' "$file" > tmp.json && mv tmp.json "$file"
+ echo "Updated $file to version $new_version"
+ else
+ echo "$file not found, skipping..."
+ fi
+done
+
+# Replace version number in manifest_ff.json and chrome
+sed -i "s/\"version\": \"$current_version\"/\"version\": \"$new_version\"/" manifest_ff.json
+sed -i "s/\"version\": \"$current_version\"/\"version\": \"$new_version\"/" manifest_chrome.json
+
+folder_name="$extension_name-$new_version"
+
+# Create a new folder with the extension name and version inside the export folder if it doesn't exist
+if [ ! -d "$export_folder/$folder_name" ]; then
+ mkdir -p "$export_folder/$folder_name"
+ echo "Created folder: $export_folder/$folder_name"
+else
+ echo "Folder $export_folder/$folder_name already exists, skipping..."
+fi
+
+# prepping firefox xpi
+cp background_ff.js background.js
+cp content_ff.js content.js
+cp manifest_ff.json manifest.json
+zip "$export_folder/$folder_name/$extension_name-$new_version.xpi" $include_files
+
+# prepping chrome file
+cp background_chrome.js background.js
+cp content_chrome.js content.js
+cp manifest_chrome.json manifest.json
+zip "$export_folder/$folder_name/$extension_name-$new_version.zip" $include_files
+
+# setting back to chrome manifest
+cp background_chrome.js background.js
+cp content_chrome.js content.js
+cp manifest_chrome.json manifest.json
\ No newline at end of file
diff --git a/1000_bots_webextension/packaged/refused_to_be_human-0.0.2/refused_to_be_human-0.0.2.xpi b/1000_bots_webextension/packaged/refused_to_be_human-0.0.2/refused_to_be_human-0.0.2.xpi
new file mode 100644
index 0000000000000000000000000000000000000000..87c64b748b6454e79e6dd476c948a174bfccf4b2
GIT binary patch
literal 10850
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
literal 0
HcmV?d00001
diff --git a/1000_bots_webextension/packaged/refused_to_be_human-0.0.2/refused_to_be_human-0.0.2.zip b/1000_bots_webextension/packaged/refused_to_be_human-0.0.2/refused_to_be_human-0.0.2.zip
new file mode 100644
index 0000000000000000000000000000000000000000..0b15515ffb2bfe22bae4b459f3f85bf699dccbf3
GIT binary patch
literal 12142
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