Better Blocks

디시인사이드에 더 나은 단어 차단 기능을 제공합니다

// ==UserScript==
// @name        Better Blocks
// @namespace   better-blocks
// @description 디시인사이드에 더 나은 단어 차단 기능을 제공합니다
// @version     0.2.0
// @author      Sangha Lee
// @copyright   2024, Sangha Lee
// @license     MIT
// @match       https://www.dcinside.com/
// @match       https://gall.dcinside.com/board/*
// @match       https://gall.dcinside.com/mgallery/board/*
// @match       https://gall.dcinside.com/mini/board/*
// @match       https://gall.dcinside.com/person/board/*
// @icon        https://nstatic.dcinside.com/dc/m/img/dcinside_icon.png
// @run-at      document-start
// @grant       GM_getValue
// @grant       GM_setValue
// ==/UserScript==

/**
 * @typedef {Object} Nodes
 * @property {ElementNode?} $parent
 * @property {string}       parentSelector
 * @property {string}       childrenSelector
 * @property {bool?}        observe
 */

/** @type {Nodes[]} */
const nodes = [
    {
        // www.dcinside.com: 실시간 베스트 게시글 목록
        parentSelector: '#dcbest_list_date',
        childrenSelector: 'li'
    },
    {
        // gall.dcinside.com: 게시글 목록
        parentSelector: '.gall_list',
        childrenSelector: 'tr',
        observe: true
    },
    {
        // gall.dcinside.com: 댓글 목록
        parentSelector: '.comment_wrap',
        childrenSelector: 'li',
        observe: true
    }
]


const url = new URL(location.href)
const gallery = url.searchParams.get('id')
const configs = {
    ...GM_getValue('configs'),
    ...JSON.parse(localStorage.getItem('block_parts') ?? '{}')
}

// 전역 설정 가져오기
configs._ = {
    ...configs._ ?? {},
    ...JSON.parse(localStorage.getItem('block_all') ?? '{}')
}

// cross-domain 불가능한 localStorage 설정을 유저스크립트 변수와 동기화하기
GM_setValue('configs', configs)

const config = {
    on: 1,
    word: '',
    id: '',
    ip: '',
    nick: '',
    ...(configs._.on === 1 ? configs._ : {}),
    ...(configs[gallery]?.on === 1 ? configs[gallery] : {})
}

function isNotEmpty (v) {
    return v !== ''
}

const blockedWords = config.word.split('||').filter(isNotEmpty)
const blockedIDs = config.ip.split('||').filter(isNotEmpty)
const blockedIPs = config.ip.split('||').filter(isNotEmpty)
const blockedNicknames = config.nick.split('||').filter(isNotEmpty)

function isblockedNode ($node) {
    // www.dcinside.com: 실시간 베스트 제목
    const $bestTitle = $node.querySelector('.besttxt')
    if ($bestTitle && blockedWords.some(v => $bestTitle.textContent.includes(v))) {
        return true
    }

    // gall.dcinside.com: 게시글 및 댓글 제목과 내용
    const $content = $node.querySelector('.ub-word') 
    if ($content && blockedWords.some(v => $content.textContent?.includes(v))) {
        return true
    }
    
    // gall.dcinside.com: 게시글 및 댓글 작성자 정보
    const $author = $node.querySelector('.ub-writer') 
    if ($author) {
        const i = $author.dataset

        switch (true) {
            case i.uid && blockedIDs.some(v => i.uid.includes(v)):
            case i.ip && blockedIPs.some(v => i.ip.includes(v)):
            case i.nick && blockedNicknames.some(v => i.nick.includes(v)):
                return true
        }
    }

    return false
}

function filterNodes ($nodes) {
    $nodes
        .filter(isblockedNode)
        .map(v => v.classList.add('block-disable'))
}

// 기존 차단 메소드 비활성화
Object.defineProperty(window, 'chk_user_block', {
    writable: false,
    value: undefined
})

document.addEventListener('DOMContentLoaded', () => {
    for (const node of nodes) {
        node.$parent = document.querySelector(node.parentSelector)
        if (node.$parent === null || node.observe !== true) {
            continue
        }

        new MutationObserver(() => filterNodes([...$parent.querySelectorAll(node.childrenSelector)]))
            .observe(node.$parent, { childList: true })
    }

    filterNodes(
        nodes
            .map(n => [...n.$parent?.querySelectorAll(n.childrenSelector) ?? []])
            .filter(v => v && v.length > 0)
            .flat()
    )
})

QingJ © 2025

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