Site Filter (Protocol-Independent)

Manage allowed sites dynamically and reference this in other scripts.

目前为 2025-02-13 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/526770/1536621/Site%20Filter%20%28Protocol-Independent%29.js

  1. // ==UserScript==
  2. // @name Site Filter (Protocol-Independent)
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.0
  5. // @description Manage allowed sites dynamically and reference this in other scripts.
  6. // @author blvdmd
  7. // @match *://*/*
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @grant GM_registerMenuCommand
  11. // @grant GM_download
  12. // @run-at document-start
  13. // ==/UserScript==
  14.  
  15. (function () {
  16. 'use strict';
  17.  
  18. // ✅ Wait for `SCRIPT_STORAGE_KEY` to be set
  19. function waitForScriptStorageKey(maxWait = 1000) {
  20. return new Promise(resolve => {
  21. const startTime = Date.now();
  22. const interval = setInterval(() => {
  23. if (typeof window.SCRIPT_STORAGE_KEY !== 'undefined') {
  24. clearInterval(interval);
  25. resolve(window.SCRIPT_STORAGE_KEY);
  26. } else if (Date.now() - startTime > maxWait) {
  27. clearInterval(interval);
  28. console.error("🚨 SCRIPT_STORAGE_KEY is not set! Make sure your script sets it **before** @require.");
  29. resolve(null);
  30. }
  31. }, 50);
  32. });
  33. }
  34.  
  35. (async function initialize() {
  36.  
  37. async function waitForDocumentReady() {
  38. if (document.readyState === "complete") return;
  39. return new Promise(resolve => {
  40. window.addEventListener("load", resolve, { once: true });
  41. });
  42. }
  43. // ✅ Wait for the script storage key
  44. const key = await waitForScriptStorageKey();
  45. if (!key) return;
  46.  
  47. // ✅ Ensure the document is fully loaded before setting `shouldRunOnThisSite`
  48. await waitForDocumentReady();
  49.  
  50. const STORAGE_KEY = `additionalSites_${key}`; // Unique per script
  51.  
  52. // function getDefaultList() {
  53. // return [
  54. // "*.example.*",
  55. // "*example2*"
  56. // ];
  57. // }
  58.  
  59. function getDefaultList() {
  60. return typeof window.GET_DEFAULT_LIST === "function" ? window.GET_DEFAULT_LIST() : [];
  61. }
  62. function normalizeUrl(url) {
  63. return url.replace(/^https?:\/\//, ''); // Remove "http://" or "https://"
  64. }
  65. // Load stored additional sites (default is an empty array)
  66. let additionalSites = GM_getValue(STORAGE_KEY, []);
  67. // Merge user-defined sites with default sites (protocols ignored)
  68. let mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);
  69. GM_registerMenuCommand("➕ Add Current Site to Include List", addCurrentSiteMenu);
  70. GM_registerMenuCommand("📜 View Included Sites", viewIncludedSites);
  71. GM_registerMenuCommand("🗑️ Delete Specific Entries", deleteEntries);
  72. GM_registerMenuCommand("✏️ Edit an Entry", editEntry);
  73. GM_registerMenuCommand("🚨 Clear All Entries", clearAllEntries);
  74. GM_registerMenuCommand("📤 Export Site List as JSON", exportAdditionalSites);
  75. GM_registerMenuCommand("📥 Import Site List from JSON", importAdditionalSites);
  76. async function shouldRunOnThisSite() {
  77. const currentFullPath = normalizeUrl(`${window.location.href}`);
  78. return mergedSites.some(pattern => wildcardToRegex(normalizeUrl(pattern)).test(currentFullPath));
  79. }
  80. // function wildcardToRegex(pattern) {
  81. // return new RegExp("^" + pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.') + "$");
  82. // }
  83. /**
  84. * Convert a wildcard pattern (e.g., "*.example.com/index.php?/forums/*") into a valid regex.
  85. * - `*` → Matches any characters (`.*`)
  86. * - `?` → Treated as a **literal question mark** (`\?`)
  87. * - `.` → Treated as a **literal dot** (`\.`)
  88. */
  89. function wildcardToRegex(pattern) {
  90. return new RegExp("^" + pattern
  91. .replace(/[-[\]{}()+^$|#\s]/g, '\\$&') // Escape regex special characters (EXCEPT `.` and `?`)
  92. .replace(/\./g, '\\.') // Ensure `.` is treated as a literal dot
  93. .replace(/\?/g, '\\?') // Ensure `?` is treated as a literal question mark
  94. .replace(/\*/g, '.*') // Convert `*` to `.*` (match any sequence)
  95. + "$");
  96. }
  97. function addCurrentSiteMenu() {
  98. const currentHost = window.location.hostname;
  99. const currentPath = window.location.pathname;
  100. const domainParts = currentHost.split('.');
  101. const baseDomain = domainParts.length > 2 ? domainParts.slice(-2).join('.') : domainParts.join('.');
  102. const secondLevelDomain = domainParts.length > 2 ? domainParts.slice(-2, -1)[0] : domainParts[0];
  103. const options = [
  104. { name: `Preferred Domain Match (*${secondLevelDomain}.*)`, pattern: `*${secondLevelDomain}.*` },
  105. { name: `Base Hostname (*.${baseDomain}*)`, pattern: `*.${baseDomain}*` },
  106. { name: `Base Domain (*.${secondLevelDomain}.*)`, pattern: `*.${secondLevelDomain}.*` },
  107. { name: `Host Contains (*${secondLevelDomain}*)`, pattern: `*${secondLevelDomain}*` },
  108. { name: `Exact Path (${currentHost}${currentPath})`, pattern: normalizeUrl(`${window.location.href}`) },
  109. { name: "Custom Wildcard Pattern", pattern: normalizeUrl(`${window.location.href}`) }
  110. ];
  111. const userChoice = prompt(
  112. "Select an option to add the site:\n" +
  113. options.map((opt, index) => `${index + 1}. ${opt.name}`).join("\n") +
  114. "\nEnter a number or cancel."
  115. );
  116. if (!userChoice) return;
  117. const selectedIndex = parseInt(userChoice, 10) - 1;
  118. if (selectedIndex >= 0 && selectedIndex < options.length) {
  119. let pattern = normalizeUrl(options[selectedIndex].pattern);
  120. if (options[selectedIndex].name === "Custom Wildcard Pattern") {
  121. pattern = normalizeUrl(prompt("Edit custom wildcard pattern:", pattern));
  122. if (!pattern.trim()) return alert("Invalid pattern. Operation canceled.");
  123. }
  124. if (!additionalSites.includes(pattern)) {
  125. additionalSites.push(pattern);
  126. GM_setValue(STORAGE_KEY, additionalSites);
  127. mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);
  128. alert(`✅ Added site with pattern: ${pattern}`);
  129. } else {
  130. alert(`⚠️ Pattern "${pattern}" is already in the list.`);
  131. }
  132. }
  133. }
  134. function viewIncludedSites() {
  135. //alert(`🔍 Included Sites:\n${mergedSites.join("\n") || "No sites added yet."}`);
  136. alert(`🔍 Included Sites:\n${additionalSites.join("\n") || "No sites added yet."}`);
  137. }
  138. function deleteEntries() {
  139. if (additionalSites.length === 0) return alert("⚠️ No user-defined entries to delete.");
  140. const userChoice = prompt("Select entries to delete (comma-separated numbers):\n" +
  141. additionalSites.map((item, index) => `${index + 1}. ${item}`).join("\n"));
  142. if (!userChoice) return;
  143. const indicesToRemove = userChoice.split(',').map(num => parseInt(num.trim(), 10) - 1);
  144. additionalSites = additionalSites.filter((_, index) => !indicesToRemove.includes(index));
  145. GM_setValue(STORAGE_KEY, additionalSites);
  146. mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);
  147. alert("✅ Selected entries have been deleted.");
  148. }
  149. function editEntry() {
  150. if (additionalSites.length === 0) return alert("⚠️ No user-defined entries to edit.");
  151. const userChoice = prompt("Select an entry to edit:\n" +
  152. additionalSites.map((item, index) => `${index + 1}. ${item}`).join("\n"));
  153. if (!userChoice) return;
  154. const selectedIndex = parseInt(userChoice, 10) - 1;
  155. if (selectedIndex < 0 || selectedIndex >= additionalSites.length) return alert("❌ Invalid selection.");
  156. const newPattern = normalizeUrl(prompt("Edit the pattern:", additionalSites[selectedIndex]));
  157. if (newPattern && newPattern.trim() && newPattern !== additionalSites[selectedIndex]) {
  158. additionalSites[selectedIndex] = newPattern.trim();
  159. GM_setValue(STORAGE_KEY, additionalSites);
  160. mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);
  161. alert("✅ Entry updated.");
  162. }
  163. }
  164. function clearAllEntries() {
  165. if (additionalSites.length === 0) return alert("⚠️ No user-defined entries to clear.");
  166. if (confirm(`🚨 You have ${additionalSites.length} entries. Clear all?`)) {
  167. additionalSites = [];
  168. GM_setValue(STORAGE_KEY, additionalSites);
  169. mergedSites = [...getDefaultList()].map(normalizeUrl);
  170. alert("✅ All user-defined entries cleared.");
  171. }
  172. }
  173. function exportAdditionalSites() {
  174. GM_download("data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(additionalSites, null, 2)), "additionalSites_backup.json");
  175. alert("📤 Additional sites exported as JSON.");
  176. }
  177. function importAdditionalSites() {
  178. const input = document.createElement("input");
  179. input.type = "file";
  180. input.accept = ".json";
  181. input.onchange = event => {
  182. const reader = new FileReader();
  183. reader.onload = e => {
  184. additionalSites = JSON.parse(e.target.result);
  185. GM_setValue(STORAGE_KEY, additionalSites);
  186. mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(normalizeUrl);
  187. alert("📥 Sites imported successfully.");
  188. };
  189. reader.readAsText(event.target.files[0]);
  190. };
  191. input.click();
  192. }
  193. window.shouldRunOnThisSite = shouldRunOnThisSite;
  194. })();
  195. })();
  196.  
  197. //To use this in another script use @require
  198. // @run-at document-end
  199. // ==/UserScript==
  200.  
  201. // window.SCRIPT_STORAGE_KEY = "magnetLinkHashChecker"; // UNIQUE STORAGE KEY
  202.  
  203.  
  204. // (async function () {
  205. // 'use strict';
  206.  
  207. // // ✅ Wait until `shouldRunOnThisSite` is available
  208. // while (typeof shouldRunOnThisSite === 'undefined') {
  209. // await new Promise(resolve => setTimeout(resolve, 50));
  210. // }
  211.  
  212. // if (!(await shouldRunOnThisSite())) return;
  213. // alert("running");
  214.  
  215.  
  216. // const OFFCLOUD_CACHE_API_URL = 'https://offcloud.com/api/cache';

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址