Guest User

Untitled

a guest
Jan 9th, 2026
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.72 KB | None | 0 0
  1. // ==UserScript==
  2. // @name AutoAdmit User Hider 2
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.0
  5. // @description Hide threads and posts from specific users on AutoAdmit
  6. // @match *://www.autoadmit.com/*
  7. // @match *://autoadmit.com/*
  8. // @match *://www.xoxohth.com/*
  9. // @match *://xoxohth.com/*
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // ============ CONFIGURATION ============
  17. const DEFAULT_BLOCKED_USERS = [
  18. 'https://imgur.com/a/o2g8xYK'
  19. // Add more usernames here
  20. ];
  21.  
  22. const FETCH_DELAY_MS = 500; // Delay between requests to avoid rate limiting
  23. const CACHE_EXPIRY_DAYS = 1; // How long to cache thread author data
  24. // ========================================
  25.  
  26. const CACHE_KEY = 'autoadmit_thread_authors';
  27. const CACHE_TIME_KEY = 'autoadmit_cache_times';
  28. const BLOCKED_USERS_KEY = 'autoadmit_blocked_users';
  29.  
  30. // Get blocked users (combines default + user-added)
  31. function getBlockedUsers() {
  32. try {
  33. const userAdded = JSON.parse(localStorage.getItem(BLOCKED_USERS_KEY) || '[]');
  34. return [...DEFAULT_BLOCKED_USERS, ...userAdded];
  35. } catch {
  36. return [...DEFAULT_BLOCKED_USERS];
  37. }
  38. }
  39.  
  40. function getUserAddedBlocklist() {
  41. try {
  42. return JSON.parse(localStorage.getItem(BLOCKED_USERS_KEY) || '[]');
  43. } catch {
  44. return [];
  45. }
  46. }
  47.  
  48. function saveUserBlocklist(list) {
  49. localStorage.setItem(BLOCKED_USERS_KEY, JSON.stringify(list));
  50. }
  51.  
  52. function addBlockedUser(username) {
  53. const list = getUserAddedBlocklist();
  54. const trimmed = username.trim();
  55. if (trimmed && !list.some(u => u.toLowerCase() === trimmed.toLowerCase())) {
  56. list.push(trimmed);
  57. saveUserBlocklist(list);
  58. return true;
  59. }
  60. return false;
  61. }
  62.  
  63. function removeBlockedUser(username) {
  64. const list = getUserAddedBlocklist();
  65. const filtered = list.filter(u => u.toLowerCase() !== username.toLowerCase());
  66. saveUserBlocklist(filtered);
  67. }
  68.  
  69. // Create the UI
  70. function createBlocklistUI() {
  71. // Container
  72. const container = document.createElement('div');
  73. container.id = 'aa-blocklist-container';
  74. container.style.cssText = 'position:fixed;bottom:10px;right:10px;z-index:9999;font-family:Arial,sans-serif;font-size:12px;';
  75.  
  76. // Toggle button
  77. const toggleBtn = document.createElement('button');
  78. toggleBtn.textContent = '🚫';
  79. toggleBtn.style.cssText = 'background:#444;color:#fff;border:none;padding:2px 4px;border-radius:5px;cursor:pointer;';
  80. container.appendChild(toggleBtn);
  81.  
  82. // Panel (hidden by default)
  83. const panel = document.createElement('div');
  84. panel.style.cssText = 'display:none;background:#333;color:#fff;padding:10px;border-radius:5px;margin-top:5px;width:250px;max-height:300px;overflow-y:auto;';
  85.  
  86. // Input row
  87. const inputRow = document.createElement('div');
  88. inputRow.style.cssText = 'display:flex;gap:5px;margin-bottom:10px;';
  89.  
  90. const input = document.createElement('input');
  91. input.type = 'text';
  92. input.placeholder = 'Enter username...';
  93. input.style.cssText = 'flex:1;padding:5px;border:none;border-radius:3px;';
  94.  
  95. const addBtn = document.createElement('button');
  96. addBtn.textContent = 'Add';
  97. addBtn.style.cssText = 'background:#5a5;color:#fff;border:none;padding:5px 10px;border-radius:3px;cursor:pointer;';
  98.  
  99. inputRow.appendChild(input);
  100. inputRow.appendChild(addBtn);
  101. panel.appendChild(inputRow);
  102.  
  103. // List container
  104. const listContainer = document.createElement('div');
  105. listContainer.id = 'aa-blocklist-list';
  106. panel.appendChild(listContainer);
  107.  
  108. container.appendChild(panel);
  109. document.body.appendChild(container);
  110.  
  111. // Render the list
  112. function renderList() {
  113. const allBlocked = getBlockedUsers();
  114. const userAdded = getUserAddedBlocklist();
  115. listContainer.innerHTML = '';
  116.  
  117. if (allBlocked.length === 0) {
  118. listContainer.innerHTML = '<div style="color:#999;">No blocked users</div>';
  119. return;
  120. }
  121.  
  122. allBlocked.forEach(username => {
  123. const row = document.createElement('div');
  124. row.style.cssText = 'display:flex;justify-content:space-between;align-items:center;padding:5px;background:#444;margin-bottom:3px;border-radius:3px;';
  125.  
  126. const nameSpan = document.createElement('span');
  127. nameSpan.textContent = username;
  128. nameSpan.style.cssText = 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px;';
  129. nameSpan.title = username;
  130.  
  131. row.appendChild(nameSpan);
  132.  
  133. // Only show remove button for user-added entries
  134. const isUserAdded = userAdded.some(u => u.toLowerCase() === username.toLowerCase());
  135. if (isUserAdded) {
  136. const removeBtn = document.createElement('button');
  137. removeBtn.textContent = '✕';
  138. removeBtn.style.cssText = 'background:#a55;color:#fff;border:none;padding:2px 6px;border-radius:3px;cursor:pointer;font-size:10px;';
  139. removeBtn.onclick = () => {
  140. removeBlockedUser(username);
  141. renderList();
  142. };
  143. row.appendChild(removeBtn);
  144. } else {
  145. const defaultBadge = document.createElement('span');
  146. defaultBadge.textContent = 'default';
  147. defaultBadge.style.cssText = 'font-size:9px;color:#999;';
  148. row.appendChild(defaultBadge);
  149. }
  150.  
  151. listContainer.appendChild(row);
  152. });
  153. }
  154.  
  155. // Toggle panel visibility
  156. toggleBtn.onclick = () => {
  157. const isVisible = panel.style.display !== 'none';
  158. panel.style.display = isVisible ? 'none' : 'block';
  159. if (!isVisible) renderList();
  160. };
  161.  
  162. // Add user
  163. function addUser() {
  164. if (addBlockedUser(input.value)) {
  165. input.value = '';
  166. renderList();
  167. // Refresh the page to apply new blocks
  168. if (confirm('User added. Reload page to apply changes?')) {
  169. location.reload();
  170. }
  171. }
  172. }
  173.  
  174. addBtn.onclick = addUser;
  175. input.onkeypress = (e) => {
  176. if (e.key === 'Enter') addUser();
  177. };
  178. }
  179.  
  180. // Initialize UI
  181. createBlocklistUI();
  182.  
  183. // Load cached data
  184. function getCache() {
  185. try {
  186. return JSON.parse(localStorage.getItem(CACHE_KEY) || '{}');
  187. } catch {
  188. return {};
  189. }
  190. }
  191.  
  192. function getCacheTimes() {
  193. try {
  194. return JSON.parse(localStorage.getItem(CACHE_TIME_KEY) || '{}');
  195. } catch {
  196. return {};
  197. }
  198. }
  199.  
  200. function saveCache(cache) {
  201. localStorage.setItem(CACHE_KEY, JSON.stringify(cache));
  202. }
  203.  
  204. function saveCacheTimes(times) {
  205. localStorage.setItem(CACHE_TIME_KEY, JSON.stringify(times));
  206. }
  207.  
  208. function isCacheExpired(threadId) {
  209. const times = getCacheTimes();
  210. if (!times[threadId]) return true;
  211. const expiryMs = CACHE_EXPIRY_DAYS * 24 * 60 * 60 * 1000;
  212. return (Date.now() - times[threadId]) > expiryMs;
  213. }
  214.  
  215. function escapeRegex(string) {
  216. return string.replace(/[.*+?^${}()|[\]\\\/]/g, '\\$&');
  217. }
  218.  
  219. function isBlockedUser(username) {
  220. if (!username) return false;
  221. const lowerUsername = username.toLowerCase().trim();
  222. const blockedUsers = getBlockedUsers();
  223. return blockedUsers.some(blocked =>
  224. lowerUsername === blocked.toLowerCase().trim()
  225. );
  226. }
  227.  
  228. // Fetch a thread page and extract the OP's username
  229. async function fetchThreadAuthor(threadId) {
  230. try {
  231. const response = await fetch(`/thread.php?thread_id=${threadId}&forum_id=2`);
  232. const html = await response.text();
  233.  
  234. // Parse the HTML to find the first author
  235. const parser = new DOMParser();
  236. const doc = parser.parseFromString(html, 'text/html');
  237.  
  238. // Find first post table (width=700, not indented)
  239. const tables = doc.querySelectorAll('table[width="700"]');
  240. for (const table of tables) {
  241. // Check if this is a top-level post (no indentation td)
  242. const firstTd = table.querySelector('td:first-child');
  243. if (firstTd && firstTd.querySelector('img[src*="blank.gif"]')) {
  244. continue; // This is a reply (indented)
  245. }
  246.  
  247. // Extract author from this table
  248. const tableHtml = table.innerHTML;
  249. const authorMatch = tableHtml.match(/<b>Author:<\/b>(?:&nbsp;|\s)*(?:<[^>]*>)*\s*([^<]+)/i);
  250. if (authorMatch) {
  251. return authorMatch[1].trim();
  252. }
  253. }
  254. return null;
  255. } catch (error) {
  256. console.error(`Error fetching thread ${threadId}:`, error);
  257. return null;
  258. }
  259. }
  260.  
  261. // Get all thread IDs from the main page
  262. function getThreadIds() {
  263. const threadIds = [];
  264. const links = document.querySelectorAll('a[href*="thread_id="]');
  265. links.forEach(link => {
  266. const match = link.href.match(/thread_id=(\d+)/);
  267. if (match) {
  268. threadIds.push({
  269. id: match[1],
  270. row: link.closest('tr')
  271. });
  272. }
  273. });
  274. // Remove duplicates
  275. const seen = new Set();
  276. return threadIds.filter(item => {
  277. if (seen.has(item.id)) return false;
  278. seen.add(item.id);
  279. return true;
  280. });
  281. }
  282.  
  283. // Hide a thread row
  284. function hideThreadRow(row) {
  285. if (row) {
  286. row.style.display = 'none';
  287. }
  288. }
  289.  
  290. // Process threads on main page
  291. async function processMainPage() {
  292. const threads = getThreadIds();
  293. const cache = getCache();
  294. const cacheTimes = getCacheTimes();
  295.  
  296. let fetchCount = 0;
  297. const toFetch = [];
  298.  
  299. // First pass: hide already-cached blocked threads, identify what needs fetching
  300. for (const thread of threads) {
  301. if (cache[thread.id] && !isCacheExpired(thread.id)) {
  302. if (isBlockedUser(cache[thread.id])) {
  303. hideThreadRow(thread.row);
  304. }
  305. } else {
  306. toFetch.push(thread);
  307. }
  308. }
  309.  
  310. // Show status
  311. let statusEl = document.getElementById('aa-hider-status');
  312. if (!statusEl && toFetch.length > 0) {
  313. statusEl = document.createElement('div');
  314. statusEl.id = 'aa-hider-status';
  315. statusEl.style.cssText = 'position:fixed;top:10px;right:10px;background:#333;color:#fff;padding:10px;border-radius:5px;z-index:9999;font-size:12px;';
  316. document.body.appendChild(statusEl);
  317. }
  318.  
  319. // Fetch unknown threads
  320. for (let i = 0; i < toFetch.length; i++) {
  321. const thread = toFetch[i];
  322.  
  323. if (statusEl) {
  324. statusEl.textContent = `Scanning threads: ${i + 1}/${toFetch.length}`;
  325. }
  326.  
  327. const author = await fetchThreadAuthor(thread.id);
  328. if (author) {
  329. cache[thread.id] = author;
  330. cacheTimes[thread.id] = Date.now();
  331.  
  332. // Save immediately after each fetch
  333. saveCache(cache);
  334. saveCacheTimes(cacheTimes);
  335.  
  336. if (isBlockedUser(author)) {
  337. hideThreadRow(thread.row);
  338. }
  339. }
  340.  
  341. // Delay between requests
  342. if (i < toFetch.length - 1) {
  343. await new Promise(resolve => setTimeout(resolve, FETCH_DELAY_MS));
  344. }
  345. }
  346.  
  347. // Remove status
  348. if (statusEl) {
  349. statusEl.textContent = 'Done!';
  350. setTimeout(() => statusEl.remove(), 2000);
  351. }
  352. }
  353.  
  354. // Hide posts within thread pages
  355. function hidePostsInThread() {
  356. const blockedUsers = getBlockedUsers();
  357. const tables = document.querySelectorAll('table[width="700"]');
  358. tables.forEach(table => {
  359. const tableHtml = table.innerHTML;
  360. for (const blockedUser of blockedUsers) {
  361. const authorPattern = new RegExp(
  362. `<b>Author:<\\/b>(?:&nbsp;|\\s)*(?:<[^>]*>)*\\s*${escapeRegex(blockedUser)}`,
  363. 'i'
  364. );
  365. if (authorPattern.test(tableHtml)) {
  366. table.style.display = 'none';
  367. let prev = table.previousElementSibling;
  368. while (prev && (prev.tagName === 'BR' || prev.tagName === 'A' || prev.tagName === 'P')) {
  369. prev.style.display = 'none';
  370. prev = prev.previousElementSibling;
  371. }
  372. break;
  373. }
  374. }
  375. });
  376.  
  377. // Hide from thread summary at top
  378. const summaryTds = document.querySelectorAll('table[cellpadding="0"][cellspacing="0"] td');
  379. summaryTds.forEach(td => {
  380. for (const blockedUser of blockedUsers) {
  381. if (td.textContent.includes(blockedUser)) {
  382. let parent = td.parentElement;
  383. if (parent) parent.style.display = 'none';
  384. }
  385. }
  386. });
  387. }
  388.  
  389. // Determine if we're on main page or thread page
  390. function isMainPage() {
  391. return window.location.pathname.includes('main.php') ||
  392. (window.location.pathname === '/' && !window.location.search.includes('thread_id'));
  393. }
  394.  
  395. function isThreadPage() {
  396. return window.location.search.includes('thread_id') ||
  397. window.location.pathname.includes('thread.php');
  398. }
  399.  
  400. // Main execution
  401. if (isMainPage()) {
  402. processMainPage();
  403. }
  404.  
  405. if (isThreadPage()) {
  406. hidePostsInThread();
  407. }
  408.  
  409. // Also run on pages that might be both (like forum listings)
  410. if (!isMainPage() && !isThreadPage()) {
  411. // Try both approaches
  412. hidePostsInThread();
  413. }
  414.  
  415. })();
Advertisement
Add Comment
Please, Sign In to add comment