Bitcoin Forum
May 22, 2026, 12:40:04 PM *
News: Latest Bitcoin Core release: 31.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Bitcointalk post filter  (Read 137 times)
mcdouglasx (OP)
Hero Member
*****
Offline

Activity: 1008
Merit: 597



View Profile WWW
May 21, 2026, 02:57:24 PM
Last edit: May 21, 2026, 11:40:19 PM by mcdouglasx
Merited by Welsh (5), klarki (3), nutildah (3), Halab (2), Mia Chloe (2), davis196 (1), dkbit98 (1), Ambatman (1)
 #1

Since some users may find navigating the forum tedious, citing spam as a problem that prevents them from enjoying the experience as they would like, in an attempt to minimize spam or make their visit more pleasant, I have added a series of filters to a Tampermonkey userscript that allows for various types of post filtering.

It includes:

1- Board filters tab: This tab allows you to select which boards will be excluded from the unread and unreadreplies sections.

2- Filtering by posts in order of most/least views and vice versa.

3- Filtering posts according to the number of merits of the OPs (this option is adjustable by us).

4-Keyword Filter: To hide posts that contain certain words in their title, based on a custom list.(new)

Code:
// ==UserScript==
// @name         Bitcointalk Board Filter + Priority (Views / Merit)
// @version      1.1
// @description  Filter topics by board, Control filters global, Keyword filtering.
// @author       Mcdouglasx
// @match        https://bitcointalk.org/index.php?action=unreadreplies*
// @match        https://bitcointalk.org/index.php?action=unread*
// @match        https://bitcointalk.org/index.php?board=*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    const boardNames = {
        1: "Bitcoin Discussion", 4: "Bitcoin Technical Support", 5: "Marketplace",
        6: "Development & Technical Discussion", 7: "Economics", 8: "Trading Discussion",
        9: "Off-topic", 10: "Pyccкий (Russian)", 11: "Other languages/locations",
        12: "Project Development", 13: "Français", 14: "Mining", 16: "Deutsch (German)",
        17: "Chinese students", 18: "Paзнoe", 19: "Юpиcты", 20: "Tpeйдepы",
        21: "Maйнepы", 22: "Hoвички", 23: "Бизнec", 24: "Meta", 25: "Obsolete (buying)",
        26: "Obsolete (selling)", 27: "Español (Spanish)", 28: "Italiano (Italian)",
        29: "Português (Portuguese)", 30: "中文 (Chinese)", 31: "Mercado y Economía",
        32: "Hardware y Minería", 33: "Esquina Libre", 34: "Politics & Society",
        35: "Biete", 36: "Suche", 37: "Wallet software", 39: "Beginners & Help",
        40: "Mining support", 41: "Pools", 42: "Mining software (miners)",
        44: "CPU/GPU Bitcoin mining hardware", 45: "Skandinavisk", 46: "Mercato valute",
        47: "Discussions générales et utilisation du Bitcoin", 48: "Mining et Hardware",
        49: "Place de marché", 50: "Hors-sujet", 51: "Goods", 52: "Services",
        53: "Currency exchange", 54: "Wiki, documentation et traduction", 55: "Xaйпы",
        56: "Gambling", 57: "Speculation", 59: "Archival", 60: "Mining (Deutsch)",
        61: "Trading und Spekulation", 62: "Anfänger und Hilfe", 63: "Projektentwicklung",
        64: "Off-Topic (Deutsch)", 65: "Lending", 66: "Кoдepы", 67: "Altcoin Discussion",
        69: "Economia & Mercado", 70: "Mineração em Geral", 71: "Games and rounds",
        72: "Aльтepнaтивныe кpиптoвaлюты", 73: "Auctions", 74: "Legal",
        75: "Computer hardware", 76: "Hardware", 77: "Press", 78: "Securities",
        79: "Nederlands (Dutch)", 80: "Markt", 81: "Mining speculation",
        82: "한국어 (Korean)", 83: "Scam Accusations", 84: "Service Announcements",
        85: "Service Discussion", 86: "Meetups", 87: "Important Announcements",
        88: "Long-term offers", 89: "India", 90: "Идeи", 91: "Пoлитикa",
        92: "Кopзинa", 93: "Digital goods", 94: "Gokken/lotterijen", 95: "עברית (Hebrew)",
        97: "Armory", 98: "Electrum", 99: "MultiBit", 100: "Bitcoin Wallet for Android",
        101: "Mercadillo", 102: "Mexico", 103: "Argentina", 104: "España",
        105: "Centroamerica y Caribe", 107: "Beni", 108: "Română (Romanian)",
        109: "Anunturi importante", 110: "Offtopic", 111: "Market", 112: "Tutoriale",
        113: "Bine ai venit!", 114: "Presa", 115: "Mining (Italiano)",
        116: "Mining (Nederlands)", 117: "跳蚤市场", 118: "山寨币", 119: "媒体",
        120: "Eλληνικά (Greek)", 121: "Mining (India)", 122: "Marketplace (India)",
        123: "Regional Languages (India)", 124: "Press & News from India",
        125: "Alt Coins (India)", 126: "Buyer/ Seller Reputations (India)",
        127: "Off-Topic (India)", 128: "Hoвocти", 129: "Reputation",
        130: "Primeros pasos y ayuda", 131: "Primeiros Passos (Iniciantes)",
        132: "Alt-Currencies (Italiano)", 133: "Türkçe (Turkish)", 134: "Brasil",
        135: "Portugal", 136: "Aγoρά", 137: "Group buys", 138: "BitcoinJ",
        139: "Treffen", 140: "Presse ", 141: "Auktionen", 142: "Polski",
        143: "Beurzen", 144: "Raduni/Meeting (Italiano)", 145: "Off-Topic (Italiano)",
        146: "挖矿", 147: "Alt Coins (Nederlands)", 148: "Off-topic (Nederlands)",
        149: "Altcoins (Français)", 150: "Meetings (Nederlands)",
        151: "Altcoins (criptomonedas alternativas)", 152: "Altcoins (Deutsch)",
        153: "Guide (Italiano)", 155: "Pazar Alanı", 156: "Madencilik",
        157: "Alternatif Kripto-Paralar", 158: "Konu Dışı", 159: "Announcements (Altcoins)",
        160: "Mining (Altcoins)", 161: "Marketplace (Altcoins)", 162: "Accuse scam/truffe",
        163: "Tablica ogłoszeń", 164: "Alternatywne kryptowaluty",
        165: "Crittografia e decentralizzazione", 166: "Minerit", 167: "New forum software",
        168: "Bitcoin Wiki", 169: "Progetti", 170: "Mercato", 171: "Servizi",
        172: "Esercizi commerciali", 173: "Hardware/Mining (Italiano)",
        174: "Yeni Başlayanlar & Yardım", 175: "Trading, analisis e speculazione",
        176: "Annunci", 177: "Minería de altcoins", 178: "Anunturi Monede Alternative",
        179: "Altcoins (Eλληνικά)", 180: "Bitcoin Haberleri", 181: "Criptomoedas Alternativas",
        182: "대체코인 Alt Coins (한국어)", 183: "Actualité et News", 184: "Vos sites et projetos",
        185: "Paбoтa", 186: "Développement et technique", 187: "Économie et spéculation",
        188: "Le Bitcoin et la loi", 189: "Ekonomi", 190: "Servisler",
        191: "Bahasa Indonesia (Indonesian)", 192: "Altcoins (Bahasa Indonesia)",
        193: "Marketplace (Bahasa Indonesia)", 194: "Mining (Bahasa Indonesia)",
        195: "Mining Discussion (Eλληνικά)", 196: "离题万里", 197: "Service Announcements (Altcoins)",
        198: "Service Discussion (Altcoins)", 199: "Pools (Altcoins)", 200: "Gambling (Italiano)",
        201: "Hrvatski (Croatian)", 202: "Servicios", 203: "Trading y especulación",
        204: "Servicios", 205: "Discussioni avanzate e sviluppo",
        206: "Desenvolvimento & Discussões Técnicas", 207: "Investor-based games",
        208: "Débutants", 209: "Échanges", 210: "Produits et services",
        211: "Petites annonces", 212: "Micro Earnings", 217: "Collectibles",
        219: "Pilipinas", 220: "Trgovina", 221: "Altcoins (Hrvatski)", 222: "Web Wallets",
        223: "Exchanges", 224: "Speculation (Altcoins)", 227: "Investigations",
        228: "Gambling discussion", 229: "Proje Geliştirme", 230: "Buluşmalar",
        231: "Mycelium", 232: "Fonlar", 234: "Invites & Accounts",
        235: "Madencilik (Alternatif Kripto-Paralar)", 236: "Бapaxoлкa", 237: "Oбмeнники",
        238: "Bounties (Altcoins)", 239: "Duyurular (Alternatif Kripto-Paralar)",
        240: "Tokens (Altcoins)", 241: "العربية (Arabic)", 242: "العملات البديلة (Altcoins)",
        243: "Altcoins (Pilipinas)", 246: "Altcoin Announcements (Eλληνικά)",
        247: "Altcoin Mining (Eλληνικά)", 248: "Toкeны", 250: "Serious discussion",
        251: "Ivory Tower", 252: "日本語 (Japanese)", 253: "إstefsarat wa as'ilat al-mubtadi'in",
        254: "Tokens (Español)", 255: "アルトコイン", 256: "Бayнти и aиpдpoпы",
        257: "Discutii Servicii", 258: "Annonces", 259: "Altcoins (Monede Alternative)",
        260: "Altcoin Announcements (Pilipinas)", 261: "Hardware wallets",
        262: "Oбcyждeниe Bitcoin (Russian)", 263: "Nowe kryptowaluty i tokeny",
        264: "Tablica ogłoszeń (altcoiny)", 265: "النقاشات", 266: "التعدين",
        267: "النقاشات الأخرى", 268: "Pamilihan", 269: "Marktplatz",
        270: "Announcements (Deutsch)", 271: "منصات التبادل", 272: "Off-topic (Hrvatski)",
        273: "Announcements (Hrvatski)", 274: "Others (Pilipinas)", 275: "Nigeria (Naija)",
        276: "Trading dan Spekulasi", 277: "Ekonomi, Politik, dan Budaya",
        278: "Topik Lainnya", 279: "Politics and society (Naija)", 280: "Off-topic (Naija)"
    };

    let hiddenSubforums = {};
    let blockedKeywords = [];
    let checkboxes = {};
    let isCollapsed = true;
    let isKeywordCollapsed = true;
    let priorityMode = 'none';
    let meritThreshold = 0;
    let enableMeritFilter = false;
    let meritCache = {};

    function loadPref() {
        try {
            hiddenSubforums = JSON.parse(GM_getValue('hiddenSubforums', '{}'));
            blockedKeywords = JSON.parse(GM_getValue('blockedKeywords', '[]'));
            isCollapsed = GM_getValue('isCollapsed', true);
            isKeywordCollapsed = GM_getValue('isKeywordCollapsed', true);
            priorityMode = GM_getValue('priorityMode', 'none');
            meritThreshold = parseInt(GM_getValue('meritThreshold', 0), 10);
            enableMeritFilter = GM_getValue('enableMeritFilter', false);
            meritCache = JSON.parse(GM_getValue('meritCache', '{}'));

            const now = Date.now();
            for (const uid in meritCache) {
                if (Object.prototype.hasOwnProperty.call(meritCache, uid) && (now - meritCache[uid].timestamp > 3600000)) {
                    delete meritCache[uid];
                }
            }
        } catch(e) { console.error("Error", e); }
    }

    function savePref() {
        GM_setValue('hiddenSubforums', JSON.stringify(hiddenSubforums));
        GM_setValue('blockedKeywords', JSON.stringify(blockedKeywords));
        GM_setValue('isCollapsed', isCollapsed);
        GM_setValue('isKeywordCollapsed', isKeywordCollapsed);
        GM_setValue('priorityMode', priorityMode);
        GM_setValue('meritThreshold', meritThreshold);
        GM_setValue('enableMeritFilter', enableMeritFilter);
        GM_setValue('meritCache', JSON.stringify(meritCache));
    }

    function getBoardId(row) {
        const link = row.querySelector('td:nth-child(3) span.smalltext a[href*="board="]');
        if (link) {
            const match = link.href.match(/board=(\d+)/);
            if (match && match[1]) return parseInt(match[1], 10);
        }
        return null;
    }

    function getViews(row) {
        const viewsCell = row.querySelector('td:nth-child(6)');
        if (viewsCell) {
            return parseInt(viewsCell.textContent.replace(/,/g, '').trim(), 10) || 0;
        }
        return 0;
    }

    function getAuthorId(row) {
        const authorLink = row.querySelector('td:nth-child(4) a');
        if (authorLink) {
            const match = authorLink.href.match(/u=(\d+)/);
            if (match && match[1]) return parseInt(match[1], 10);
        }
        return null;
    }

    function fetchMerit(userId) {
        return new Promise(function(resolve) {
            const now = Date.now();
            if (meritCache[userId] && (now - meritCache[userId].timestamp < 3600000)) {
                resolve(meritCache[userId].merit);
                return;
            }

            const url = 'https://bitcointalk.org/index.php?action=merit;u=' + userId;
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                window: true,
                anonymous: false,
                onload: function(resp) {
                    let merit = 0;
                    if (resp.status === 200) {
                        const html = resp.responseText;
                        const match = html.match(/<p>\s*Merit:\s*([0-9,]+)\s*<\/p>/i) || html.match(/Merit:\s*([0-9,]+)/i);
                        if (match) {
                            merit = parseInt(match[1].replace(/,/g, ''), 10);
                        }
                    }
                    meritCache[userId] = { merit: merit, timestamp: Date.now() };
                    savePref();
                    resolve(merit);
                },
                onerror: function(err) {
                    console.error('Error' + userId, err);
                    meritCache[userId] = { merit: 0, timestamp: Date.now() };
                    savePref();
                    resolve(0);
                }
            });
        });
    }

    async function fetchAllMerits(rows) {
        const uniqueAuthors = {};
        for (let i = 0; i < rows.length; i++) {
            const uid = getAuthorId(rows[i]);
            if (uid && !uniqueAuthors[uid]) {
                uniqueAuthors[uid] = rows[i];
            }
        }
        const authorIds = Object.keys(uniqueAuthors);
        if (authorIds.length === 0) return;
        const promises = [];
        for (let i = 0; i < authorIds.length; i++) {
            promises.push(fetchMerit(parseInt(authorIds[i], 10)));
        }
        await Promise.all(promises);
    }

    async function runPipeline() {
        const mainTable = document.querySelector('table.bordercolor[cellspacing="1"][cellpadding="4"]');
        if (!mainTable) return;
        const tbody = mainTable.querySelector('tbody');
        if (!tbody) return;

        const allRows = Array.from(tbody.querySelectorAll('tr:not(.titlebg)'));
        if (allRows.length === 0) return;

        const isUnreadPage = window.location.search.includes('action=unread');

        let currentRows = allRows.filter(row => {
            // Filter Boards
            if (isUnreadPage) {
                const boardId = getBoardId(row);
                const isBoardHidden = boardId && hiddenSubforums[boardId];
                if (isBoardHidden) {
                    row.style.display = 'none';
                    return false;
                }
            }
            // Filter Keywords
            const titleLink = row.querySelector('td:nth-child(3) a');
            if (titleLink) {
                const titleText = titleLink.textContent.toLowerCase();
                for (let i = 0; i < blockedKeywords.length; i++) {
                    if (titleText.includes(blockedKeywords[i].toLowerCase())) {
                        row.style.display = 'none';
                        return false;
                    }
                }
            }

            row.style.display = '';
            return true;
        });

        if (enableMeritFilter) {
            const msgDiv = document.createElement('div');
            msgDiv.textContent = 'Verifying OP Merit requirements...';
            msgDiv.style.cssText = 'background:#ffffcc;padding:5px;margin:5px 0;border:1px solid #ccc;text-align:center;font-weight:bold;color:#000;';
            const priorityPanel = document.querySelector('#priorityFilter');
            if (priorityPanel) priorityPanel.parentNode.insertBefore(msgDiv, priorityPanel.nextSibling);

            await fetchAllMerits(currentRows);
            if (msgDiv && msgDiv.parentNode) msgDiv.remove();

            currentRows = currentRows.filter(row => {
                const uid = getAuthorId(row);
                const userMerit = (uid && meritCache[uid]) ? parseInt(meritCache[uid].merit, 10) : 0;
                if (userMerit < meritThreshold) {
                    row.style.display = 'none';
                    return false;
                }
                return true;
            });
        }

        if (priorityMode === 'views') {
            currentRows.sort((a, b) => getViews(b) - getViews(a));
        } else if (priorityMode === 'views_asc') {
            currentRows.sort((a, b) => getViews(a) - getViews(b));
        }

        for (let i = 0; i < currentRows.length; i++) {
            tbody.appendChild(currentRows[i]);
        }
    }

    function buildBoardFilterUI() {
        const container = document.createElement('div');
        container.id = 'subforumFilter';
        container.style.cssText = 'background:#e9f1f6;padding:8px;margin:10px 0;border:1px solid #c9d2d9;border-radius:3px;color:#000;font-family:Verdana,sans-serif;font-size:13px;box-shadow:0 1px 3px rgba(0,0,0,0.1);';

        const header = document.createElement('div');
        header.style.display = 'flex'; header.style.justifyContent = 'space-between'; header.style.alignItems = 'center'; header.style.marginBottom = '5px';
        const title = document.createElement('h3'); title.textContent = 'Board Filter'; title.style.cssText = 'margin:0; color:#333; font-size:14px; font-weight:bold;';
        const toggleBtn = document.createElement('button'); toggleBtn.id = 'toggleBtn'; toggleBtn.style.cssText = 'padding:4px 8px;background:#3a6ea5;color:white;border:none;border-radius:3px;cursor:pointer;font-size:11px;font-weight:bold;';

        header.appendChild(title); header.appendChild(toggleBtn); container.appendChild(header);
        const content = document.createElement('div'); content.id = 'filterContent'; content.style.display = 'none';
        const grid = document.createElement('div'); grid.style.cssText = 'display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:8px;max-height:300px;overflow-y:auto;padding:5px;margin-bottom:10px;border:1px solid #c9d2d9;border-radius:3px;background:#fff;';

        const boardEntries = Object.entries(boardNames).sort((a, b) => a[1].localeCompare(b[1]));
        for (let i = 0; i < boardEntries.length; i++) {
            const id = boardEntries[i][0]; const name = boardEntries[i][1];
            const label = document.createElement('label'); label.style.cssText = 'display:flex;align-items:center;margin-bottom:4px;font-size:12px;color:#333;cursor:pointer;';
            const cb = document.createElement('input'); cb.type = 'checkbox'; cb.value = id; cb.checked = hiddenSubforums[id] || false; cb.style.marginRight = '6px';
            checkboxes[id] = cb; label.appendChild(cb); label.appendChild(document.createTextNode(name)); grid.appendChild(label);
        }
        content.appendChild(grid);
        const btnContainer = document.createElement('div'); btnContainer.style.display = 'flex'; btnContainer.style.gap = '8px';

        const applyBtn = document.createElement('button'); applyBtn.textContent = 'Apply Board Changes'; applyBtn.style.cssText = 'padding:6px 12px;background:#3a6ea5;color:white;border:none;border-radius:3px;cursor:pointer;font-size:12px;font-weight:bold;';
        applyBtn.addEventListener('click', function() {
            for (const id in checkboxes) { if (Object.prototype.hasOwnProperty.call(checkboxes, id)) hiddenSubforums[id] = checkboxes[id].checked; }
            savePref(); runPipeline().catch(err => console.error(err));
        });

        btnContainer.appendChild(applyBtn); content.appendChild(btnContainer); container.appendChild(content);
        toggleBtn.addEventListener('click', function() {
            isCollapsed = !isCollapsed; savePref(); content.style.display = isCollapsed ? 'none' : 'block';
            toggleBtn.innerHTML = isCollapsed ? '&#9654; Show Filter' : '&#9660; Hide Filter';
        });
        return container;
    }

    function buildKeywordFilterUI() {
        const container = document.createElement('div');
        container.id = 'keywordFilter';
        container.style.cssText = 'background:#f6e9e9;padding:8px;margin:10px 0;border:1px solid #d9c9c9;border-radius:3px;color:#000;font-family:Verdana,sans-serif;font-size:13px;box-shadow:0 1px 3px rgba(0,0,0,0.1);';

        const header = document.createElement('div');
        header.style.display = 'flex'; header.style.justifyContent = 'space-between'; header.style.alignItems = 'center'; header.style.marginBottom = '5px';
        const title = document.createElement('h3'); title.textContent = 'Keyword Filter'; title.style.cssText = 'margin:0; color:#333; font-size:14px; font-weight:bold;';
        const toggleBtn = document.createElement('button'); toggleBtn.id = 'toggleKeywordBtn'; toggleBtn.style.cssText = 'padding:4px 8px;background:#a53a3a;color:white;border:none;border-radius:3px;cursor:pointer;font-size:11px;font-weight:bold;';

        header.appendChild(title); header.appendChild(toggleBtn); container.appendChild(header);
        const content = document.createElement('div'); content.id = 'keywordContent'; content.style.display = 'none';

        const inputArea = document.createElement('div'); inputArea.style.marginBottom = '8px';
        const input = document.createElement('input'); input.type = 'text'; input.placeholder = 'Add keyword...'; input.style.cssText = 'padding:4px; width:70%;';
        const addBtn = document.createElement('button'); addBtn.textContent = 'Add'; addBtn.style.cssText = 'padding:4px 8px; margin-left:5px; cursor:pointer;';
        inputArea.appendChild(input); inputArea.appendChild(addBtn); content.appendChild(inputArea);

        const tagsContainer = document.createElement('div'); tagsContainer.id = 'tagsContainer'; tagsContainer.style.cssText = 'display:flex; flex-wrap:wrap; gap:5px; margin-top:5px;';
        content.appendChild(tagsContainer);

        function refreshTags() {
            tagsContainer.innerHTML = '';
            blockedKeywords.forEach((kw, index) => {
                const tag = document.createElement('span');
                tag.style.cssText = 'background:#ddd; padding:2px 6px; border-radius:3px; cursor:pointer; font-size:11px;';
                tag.textContent = kw + ' [x]';
                tag.onclick = () => { blockedKeywords.splice(index, 1); savePref(); refreshTags(); runPipeline(); };
                tagsContainer.appendChild(tag);
            });
        }

        addBtn.onclick = () => {
            if (input.value.trim() !== '') { blockedKeywords.push(input.value.trim()); input.value = ''; savePref(); refreshTags(); runPipeline(); }
        };

        refreshTags();
        container.appendChild(content);

        toggleBtn.addEventListener('click', function() {
            isKeywordCollapsed = !isKeywordCollapsed; savePref(); content.style.display = isKeywordCollapsed ? 'none' : 'block';
            toggleBtn.innerHTML = isKeywordCollapsed ? '&#9654; Show Keywords' : '&#9660; Hide Keywords';
        });
        return container;
    }

    function buildPriorityUI() {
        const container = document.createElement('div');
        container.id = 'priorityFilter';
        container.style.cssText = 'background:#e9f1f6;padding:10px;margin:10px 0;border:1px solid #c9d2d9;border-radius:3px;font-family:Verdana,sans-serif;font-size:13px;color:#000;';
        const title = document.createElement('h3'); title.textContent = 'Control filters'; title.style.cssText = 'margin:0 0 10px 0; color:#333; font-size:14px; font-weight:bold;';
        const flexBlock = document.createElement('div'); flexBlock.style.cssText = 'display:flex; flex-wrap:wrap; gap:15px; align-items:center;';

        const orderWrapper = document.createElement('div'); orderWrapper.innerHTML = '<strong>Sorting Order: </strong>';
        const modeSelect = document.createElement('select'); modeSelect.id = 'priorityModeSelect'; modeSelect.style.cssText = 'padding:4px; margin-left:5px; cursor:pointer;';
        const noneOpt = document.createElement('option'); noneOpt.value = 'none'; noneOpt.text = 'None (Default Forum Order)';
        const viewsOpt = document.createElement('option'); viewsOpt.value = 'views'; viewsOpt.text = 'Most Views First (High → Low)';
        const viewsAscOpt = document.createElement('option'); viewsAscOpt.value = 'views_asc'; viewsAscOpt.text = 'Least Views First (Low → High)';
        modeSelect.appendChild(noneOpt); modeSelect.appendChild(viewsOpt); modeSelect.appendChild(viewsAscOpt); modeSelect.value = priorityMode; orderWrapper.appendChild(modeSelect);

        const meritWrapper = document.createElement('div'); meritWrapper.style.cssText = 'display:flex; align-items:center; gap:6px; border-left:2px solid #ccc; padding-left:15px;';
        const meritCheck = document.createElement('input'); meritCheck.type = 'checkbox'; meritCheck.id = 'enableMeritFilterCheck'; meritCheck.checked = enableMeritFilter; meritCheck.style.cursor = 'pointer';
        const meritLabel = document.createElement('label'); meritLabel.htmlFor = 'enableMeritFilterCheck'; meritLabel.innerHTML = '<strong>Exclude topics if OP merit is below:</strong>';
        const meritInput = document.createElement('input'); meritInput.type = 'number'; meritInput.id = 'meritThresholdInput'; meritInput.value = meritThreshold; meritInput.style.cssText = 'width:65px; padding:3px; text-align:center;';
        meritWrapper.appendChild(meritCheck); meritWrapper.appendChild(meritLabel); meritWrapper.appendChild(meritInput);

        const applyPriorityBtn = document.createElement('button'); applyPriorityBtn.textContent = 'Apply & Refresh Feed'; applyPriorityBtn.style.cssText = 'padding:6px 14px; background:#2d5580; color:white; border:none; border-radius:3px; cursor:pointer; font-size:12px; font-weight:bold; box-shadow: 0 1px 2px rgba(0,0,0,0.2); margin-left:auto;';
        applyPriorityBtn.addEventListener('click', function() {
            priorityMode = modeSelect.value; enableMeritFilter = meritCheck.checked;
            let val = parseInt(meritInput.value, 10); if (isNaN(val) || val < 0) val = 0; meritThreshold = val;
            savePref(); window.location.reload();
        });

        flexBlock.appendChild(orderWrapper); flexBlock.appendChild(meritWrapper); flexBlock.appendChild(applyPriorityBtn);
        container.appendChild(title); container.appendChild(flexBlock); return container;
    }

    function init() {
        loadPref();
        const bodyArea = document.getElementById('bodyarea');
        if (!bodyArea) return;

        const isUnreadPage = window.location.search.includes('action=unread');

        if (isUnreadPage) {
            const boardFilter = buildBoardFilterUI();
            bodyArea.insertBefore(boardFilter, bodyArea.firstChild);
        }

        const keywordFilter = buildKeywordFilterUI();
        bodyArea.insertBefore(keywordFilter, bodyArea.firstChild);

        const priorityFilter = buildPriorityUI();
        if (isUnreadPage) {
            const boardFilterElement = document.getElementById('subforumFilter');
            bodyArea.insertBefore(priorityFilter, boardFilterElement.nextSibling);
        } else {
            bodyArea.insertBefore(priorityFilter, bodyArea.firstChild);
        }

        const contentDiv = document.getElementById('filterContent');
        const toggleBtn = document.getElementById('toggleBtn');
        if (contentDiv && toggleBtn) {
            contentDiv.style.display = isCollapsed ? 'none' : 'block';
            toggleBtn.innerHTML = isCollapsed ? '&#9654; Show Filter' : '&#9660; Hide Filter';
        }

        const kwContentDiv = document.getElementById('keywordContent');
        const kwToggleBtn = document.getElementById('toggleKeywordBtn');
        if (kwContentDiv && kwToggleBtn) {
            kwContentDiv.style.display = isKeywordCollapsed ? 'none' : 'block';
            kwToggleBtn.innerHTML = isKeywordCollapsed ? '&#9654; Show Keywords' : '&#9660; Hide Keywords';
        }

        runPipeline().catch(err => console.error('Pipeline Error:', err));
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();


██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██



██
██
██
██
██
██
██



██
██
██
██
██



██
██

██
██
██
██
██
██
██
██
██
██
███████▄▄███████▄▄
████▄███████████████▄█████▄▄▄
██▄███████████████████▄▄██▀████▄▄▄▄▄▄▄▄███▄██████
▄███████████████████▀▄█████▄▄███████████▄▀▀▀██▄██
▄███▐███████████████▄▄▀███▀███▄█████████████▄███████
████▐██████████████████▀██▄▀██▐██▄▄▄▄██▀███▀▀███▀▀▀
█████████████████████▌▄▄▄██▐██▐██▀▀▀▀███████████
███████▌█████████▐██████▄▀██▄▀█████████████████████▄
▀██▐███▌█████████▐███▀████████▄██████████▀███████████
▀█▐█████████████████▀▀▀███▀██▀▀▀▀▀▀▀▀▀██▀▀▀███▀▀▀▀▀
██▀███████████████████▀▄██▀
████▀███████████████▀
███████▀▀███████▀▀
██
██


██
██
██
██
██
██
██
██
██

██
██
██


██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
 
    FAST    🔒 SECURE    🛡️ NO KYC        EXCHANGE NOW      
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██

██
██
██
██
██
██


██
██
██
██
██
██
██
██
██
██

██
██
██
██
██
██
██
██
██
██
██
dkbit98
Legendary
*
Offline

Activity: 2968
Merit: 8683


AntiSwap.io - NO AML/KYC EXCHANGER MONITORING


View Profile WWW
May 21, 2026, 07:11:36 PM
 #2

This userscriot sounds interesting, and I am already using something similar (so I decided to try yours.
For some reason I don't have any option for Board Filter with my Violentmonkey extension.
I do see Control filters with sorting order, but I have no need for this.

PS
I am currently using this userscript made by NLNicoo:
https://github.com/NLNicoo/itob


Code:
[center][table][tr][td][font=Arial Black][size=24pt][glow=#222,1][nbsp][url=https://en.antiswap.io/?utm_source=bitcointalk_s3][size=5pt][sup][size=21pt][b][color=#03adfd]🛡[/b][/sup][/size][size=13pt][nbsp][/size][size=5pt][sup][size=18pt][color=#fff]Anti[color=#3b82f6]Swap[/sup][/size][nbsp][nbsp][size=14pt][sup][size=8pt][i][color=#fff]NO[nbsp]AML/KYC—EXCHANGER[nbsp]MONITORING[/sup][/size][nbsp][nbsp][size=6pt][sup][size=16pt][glow=#03adfd,1][nbsp][font=Impact][color=#fff]900+[/font][nbsp][/glow][/size][/sup][/size][size=6pt][sup][size=16pt][glow=#3b82f6,1][nbsp][size=8pt][sup][size=8pt][color=#fff]EXCHANGERS[/size][/sup][/size][nbsp][/glow][/size][/sup][/size][/url][nbsp][nbsp][font=Arial][b][size=14pt][sup][size=8pt][url=https://bitcointalk.org/index.php?topic=5568680.msg66184227#msg66184227][color=#fff]BITCOINTALK[/url][/size][/sup][/size][/font][nbsp][size=9pt][sup][size=18pt][color=#3b82f6]│[/size][/sup][/size][nbsp][font=Arial][b][size=14pt][sup][size=8pt][url=https://t.me/+qGCCD6ncnctiZTli][color=#fff]TELEGRAM[/url][/size][/sup][/size][/font][nbsp][nbsp][/td][/tr][/table][/center]
Ambatman
Legendary
*
Offline

Activity: 1008
Merit: 1313


Don't tell anyone


View Profile WWW
May 21, 2026, 07:11:42 PM
 #3

I don't know a good initiative imo and there are people that would need it but
I believe spams are not known by their number of views or merit
We have seen good post without merit and ones with.
It's going to limit one's experience in navigating the forum if these are the criteria.
I have noticed most time generic post tend to have more responses except on rare occasions.
 
This userscriot sounds interesting, and I am already using something similar (so I decided to try yours.
For some reason I don't have any option for Board Filter with my Violentmonkey extension.

And it's possible to ignore a board via just clicking profile
And clicking at ignore board section at the left section.

███████████████████████████
███████▄████████████▄██████
████████▄████████▄████████
███▀█████▀▄███▄▀█████▀███
█████▀█▀▄██▀▀▀██▄▀█▀█████
███████▄███████████▄███████
███████████████████████████
███████▀███████████▀███████
████▄██▄▀██▄▄▄██▀▄██▄████
████▄████▄▀███▀▄████▄████
██▄███▀▀█▀██████▀█▀███▄███
██▀█▀████████████████▀█▀███
███████████████████████████
.
.Duelbits PREDICT..
█████████████████████████
█████████████████████████
███████████▀▀░░░░▀▀██████
██████████░░▄████▄░░████
█████████░░████████░░████
█████████░░████████░░████
█████████▄▀██████▀▄████
████████▀▀░░░▀▀▀▀░░▄█████
██████▀░░░░██▄▄▄▄████████
████▀░░░░▄███████████████
█████▄▄█████████████████
█████████████████████████
█████████████████████████
.
.WHERE EVERYTHING IS A MARKET..
█████
██
██







██
██
██████
Will Bitcoin hit $200,000
before January 1st 2027?

    No @1.15         Yes @6.00    
█████
██
██







██
██
██████

  CHECK MORE > 
dkbit98
Legendary
*
Offline

Activity: 2968
Merit: 8683


AntiSwap.io - NO AML/KYC EXCHANGER MONITORING


View Profile WWW
May 21, 2026, 07:18:42 PM
 #4

And it's possible to ignore a board via just clicking profile
And clicking at ignore board section at the left section.
I know that obviously, but I want to hide individual TOPICS similar like in NLNicoo userscript or better.
And on main bitcointalk page I don't see any custom options from OP script.
 


Code:
[center][table][tr][td][font=Arial Black][size=24pt][glow=#222,1][nbsp][url=https://en.antiswap.io/?utm_source=bitcointalk_s3][size=5pt][sup][size=21pt][b][color=#03adfd]🛡[/b][/sup][/size][size=13pt][nbsp][/size][size=5pt][sup][size=18pt][color=#fff]Anti[color=#3b82f6]Swap[/sup][/size][nbsp][nbsp][size=14pt][sup][size=8pt][i][color=#fff]NO[nbsp]AML/KYC—EXCHANGER[nbsp]MONITORING[/sup][/size][nbsp][nbsp][size=6pt][sup][size=16pt][glow=#03adfd,1][nbsp][font=Impact][color=#fff]900+[/font][nbsp][/glow][/size][/sup][/size][size=6pt][sup][size=16pt][glow=#3b82f6,1][nbsp][size=8pt][sup][size=8pt][color=#fff]EXCHANGERS[/size][/sup][/size][nbsp][/glow][/size][/sup][/size][/url][nbsp][nbsp][font=Arial][b][size=14pt][sup][size=8pt][url=https://bitcointalk.org/index.php?topic=5568680.msg66184227#msg66184227][color=#fff]BITCOINTALK[/url][/size][/sup][/size][/font][nbsp][size=9pt][sup][size=18pt][color=#3b82f6]│[/size][/sup][/size][nbsp][font=Arial][b][size=14pt][sup][size=8pt][url=https://t.me/+qGCCD6ncnctiZTli][color=#fff]TELEGRAM[/url][/size][/sup][/size][/font][nbsp][nbsp][/td][/tr][/table][/center]
mcdouglasx (OP)
Hero Member
*****
Offline

Activity: 1008
Merit: 597



View Profile WWW
May 21, 2026, 09:05:34 PM
Last edit: May 21, 2026, 09:18:42 PM by mcdouglasx
Merited by vapourminer (1)
 #5

For some reason I don't have any option for Board Filter with my Violentmonkey extension.

I installed it with violentmonkey on Edge and it works fine. The board filters option is only available in the unread replies and recent posts tabs; it's not needed on individual boards, which is why it's not shown.

I don't know a good initiative imo and there are people that would need it but
I believe spams are not known by their number of views or merit
We have seen good post without merit and ones with.
It's going to limit one's experience in navigating the forum if these are the criteria.
I have noticed most time generic post tend to have more responses except on rare occasions.

You're right, this doesn't eliminate spam, as it's very ambiguous. It might help in certain cases, but with limitations. If you choose to limit by merit, you save on spam that mostly comes from new users, but you sacrifice those new users who do bring valuable content, even if they are fewer in number. Users with many merits can also spam, but it's less likely as their number of merits increases.

looking at it from another point

If you filter by merit > 0, you lose 95% of the spam and perhaps 5% of the valuable content from new users.

If you don't filter, you lose all the spam plus the valuable content because the noise can obscure it.

And it's worth noting that this 5% of valuable content is recoverable, because the user could quickly surpass the merit threshold, since not everyone will use this script.

██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██



██
██
██
██
██
██
██



██
██
██
██
██



██
██

██
██
██
██
██
██
██
██
██
██
███████▄▄███████▄▄
████▄███████████████▄█████▄▄▄
██▄███████████████████▄▄██▀████▄▄▄▄▄▄▄▄███▄██████
▄███████████████████▀▄█████▄▄███████████▄▀▀▀██▄██
▄███▐███████████████▄▄▀███▀███▄█████████████▄███████
████▐██████████████████▀██▄▀██▐██▄▄▄▄██▀███▀▀███▀▀▀
█████████████████████▌▄▄▄██▐██▐██▀▀▀▀███████████
███████▌█████████▐██████▄▀██▄▀█████████████████████▄
▀██▐███▌█████████▐███▀████████▄██████████▀███████████
▀█▐█████████████████▀▀▀███▀██▀▀▀▀▀▀▀▀▀██▀▀▀███▀▀▀▀▀
██▀███████████████████▀▄██▀
████▀███████████████▀
███████▀▀███████▀▀
██
██


██
██
██
██
██
██
██
██
██

██
██
██


██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
 
    FAST    🔒 SECURE    🛡️ NO KYC        EXCHANGE NOW      
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██

██
██
██
██
██
██


██
██
██
██
██
██
██
██
██
██

██
██
██
██
██
██
██
██
██
██
██
Mia Chloe
Legendary
*
Offline

Activity: 1078
Merit: 2205


Contact me for your designs...


View Profile
May 21, 2026, 09:38:41 PM
 #6

~snip
Thumbs up for creating this extension and we already have quite a lot of them around too. Anyways personally I don't really think I'd honestly want to filter posts for highest merits to write in. Most times I'm just a fan of nice topics that don't have too many replies and I'm good.

I noticed not every nice post packs a ton of merit in some cases although it's still kinda rare though. I think the very first feature you mentioned is likely going to be the one that will come in handy the most. A lot of users don't even know that there are other filter types on the forum too.

███████████████████████████
███████▄████████████▄██████
████████▄████████▄████████
███▀█████▀▄███▄▀█████▀███
█████▀█▀▄██▀▀▀██▄▀█▀█████
███████▄███████████▄███████
███████████████████████████
███████▀███████████▀███████
████▄██▄▀██▄▄▄██▀▄██▄████
████▄████▄▀███▀▄████▄████
██▄███▀▀█▀██████▀█▀███▄███
██▀█▀████████████████▀█▀███
███████████████████████████
.
.Duelbits PREDICT..
█████████████████████████
█████████████████████████
███████████▀▀░░░░▀▀██████
██████████░░▄████▄░░████
█████████░░████████░░████
█████████░░████████░░████
█████████▄▀██████▀▄████
████████▀▀░░░▀▀▀▀░░▄█████
██████▀░░░░██▄▄▄▄████████
████▀░░░░▄███████████████
█████▄▄█████████████████
█████████████████████████
█████████████████████████
.
.WHERE EVERYTHING IS A MARKET..
█████
██
██







██
██
██████
Will Bitcoin hit $200,000
before January 1st 2027?

    No @1.15         Yes @6.00    
█████
██
██







██
██
██████

  CHECK MORE > 
Ambatman
Legendary
*
Offline

Activity: 1008
Merit: 1313


Don't tell anyone


View Profile WWW
May 21, 2026, 10:41:08 PM
 #7

If you choose to limit by merit, you save on spam that mostly comes from new users, but you sacrifice those new users who do bring valuable content, even if they are fewer in number. Users with many merits can also spam, but it's less likely as their number of merits increases.
Ooh Now I get what you mean
Had to reread your thread
The filter is on the user and not the postt
Which would definitely affect minority that would mean nothing much in the grand scheme of things.
And if they remain under the filter it means that they may not have posts worth reading
Noted.
Thanks for clarification


Would it be too extreme adding filtering words? I think I might be a little tired of the word investor  Cheesy

███████████████████████████
███████▄████████████▄██████
████████▄████████▄████████
███▀█████▀▄███▄▀█████▀███
█████▀█▀▄██▀▀▀██▄▀█▀█████
███████▄███████████▄███████
███████████████████████████
███████▀███████████▀███████
████▄██▄▀██▄▄▄██▀▄██▄████
████▄████▄▀███▀▄████▄████
██▄███▀▀█▀██████▀█▀███▄███
██▀█▀████████████████▀█▀███
███████████████████████████
.
.Duelbits PREDICT..
█████████████████████████
█████████████████████████
███████████▀▀░░░░▀▀██████
██████████░░▄████▄░░████
█████████░░████████░░████
█████████░░████████░░████
█████████▄▀██████▀▄████
████████▀▀░░░▀▀▀▀░░▄█████
██████▀░░░░██▄▄▄▄████████
████▀░░░░▄███████████████
█████▄▄█████████████████
█████████████████████████
█████████████████████████
.
.WHERE EVERYTHING IS A MARKET..
█████
██
██







██
██
██████
Will Bitcoin hit $200,000
before January 1st 2027?

    No @1.15         Yes @6.00    
█████
██
██







██
██
██████

  CHECK MORE > 
mcdouglasx (OP)
Hero Member
*****
Offline

Activity: 1008
Merit: 597



View Profile WWW
May 21, 2026, 11:45:46 PM
 #8

Ooh Now I get what you mean
Had to reread your thread
The filter is on the user and not the postt
Which would definitely affect minority that would mean nothing much in the grand scheme of things.
And if they remain under the filter it means that they may not have posts worth reading
Noted.
Thanks for clarification


Would it be too extreme adding filtering words? I think I might be a little tired of the word investor  Cheesy

Yes, it's due to the merits of the users of the posts, not the posts themselves.

I've done what you asked; I've updated the post and the code, but it only works if the word is in the post title. You can include any words you want by adding them to your own custom list. I suppose that's what you were looking for.

██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██



██
██
██
██
██
██
██



██
██
██
██
██



██
██

██
██
██
██
██
██
██
██
██
██
███████▄▄███████▄▄
████▄███████████████▄█████▄▄▄
██▄███████████████████▄▄██▀████▄▄▄▄▄▄▄▄███▄██████
▄███████████████████▀▄█████▄▄███████████▄▀▀▀██▄██
▄███▐███████████████▄▄▀███▀███▄█████████████▄███████
████▐██████████████████▀██▄▀██▐██▄▄▄▄██▀███▀▀███▀▀▀
█████████████████████▌▄▄▄██▐██▐██▀▀▀▀███████████
███████▌█████████▐██████▄▀██▄▀█████████████████████▄
▀██▐███▌█████████▐███▀████████▄██████████▀███████████
▀█▐█████████████████▀▀▀███▀██▀▀▀▀▀▀▀▀▀██▀▀▀███▀▀▀▀▀
██▀███████████████████▀▄██▀
████▀███████████████▀
███████▀▀███████▀▀
██
██


██
██
██
██
██
██
██
██
██

██
██
██


██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██
 
    FAST    🔒 SECURE    🛡️ NO KYC        EXCHANGE NOW      
██
██
██
██
██
██
██
██
██
██
██
██
██
██
██

██
██
██
██
██
██


██
██
██
██
██
██
██
██
██
██

██
██
██
██
██
██
██
██
██
██
██
MusaMohamed
Sr. Member
****
Offline

Activity: 1512
Merit: 433



View Profile
Today at 03:40:34 AM
 #9

It includes:

2- Filtering by posts in order of most/least views and vice versa.

3- Filtering posts according to the number of merits of the OPs (this option is adjustable by us).
Some other userscripts that can support your filtering userscript.
[Userscript] Bitcointalk Post Filter
Quote
This userscript allows hiding likely low-quality posts on the Bitcointalk forum.
It works by first calculating earned merit (eMerit) of the user, then calculating the score
of their post

The script makes the following changes to the page:

- Display eMerit below the normal Merit
- Hide all posts with the score less than 1000
- Add Show/Hide button to hidden posts
- Adds Whitelist button to all posts

Bash script to filter new topics created by high rank members only

Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!