코네 도배 게시글 보이는걸 방지하기 위해 에드블록으로 시작했다가 ,
에드블록의 한계를 느꼈는데, 다른 유저분이 스크립을 써주시더라구요 .
제가 스크립은 좀 초보라 이걸 어떻게 하면 되나 알아보니, 아주 쉽더군요.
기존에 있던 지식과 새로운 정보로 스크립을 다시 쓰고 기능을 늘렸습니다.
초보라도 쉽게 하는게 가능하니, 필터링 하고 싶은게 있으시면 한번 해보시는걸 권합니다.
이 스크립은, 애드온 하나와 간단한 RegExp만 알면 사용이 가능합니다.
자바스크립을 알지 않아도 이 스크립 사용이 가능하지만, 알아두면 인터넷 활동도 더 편리해지니 이것도 시간이 되실때 더욱 트라이 해보는걸 권장합니다.
우선 'Tampermonkey' 라는 브라우저 애드온 확장 기능을 설치합시다.
Tampermonkey 는 브라우저 스크립 관리 애드온이며, 특정 사이트 링크에 접속시 스크립이 가동됩니다.
여러 스크립을 동시에 켜둘수 있으니, 여러 사이트에서 활성화가 가능합니다.
(Tampermonkey 사용법은 쉽게 검색해 알아낼수 있으니 여기에 적어드리진 않겠습니다)
(다른 방법이나 다른 애드온도 있지만, 이게 많이 사용되는 방법이니 이걸 권합니다)
Tampermonkey와 스크립은 끄지 않는 이상 브라우저와 같이 자동으로 시작되니, 브라우저 시작시 일일이 켜줄 필요가 없습니다.
스크립을 Tampermonkey에 넣고, 아래에 동일한 주석 줄들의 하단에 원하는 필터들을 넣고 저장.
필터링을 유연하게 가능케 하고자, 필터 매치는 RegExp 를 사용하는걸로 합니다.
예시가 될 RegExp를 적어놨으니 대체로 따로 알아볼 필요는 없겠지만, 필요하다면 AI한테 물어보면 만들어주니, 쉽게 사용 가능합니다.
테스트로 코네 게시판 열어서 새로고침 해가며 바로 확인해보세요.
필터링 되는 게시글은 형식을 완전 배제하지 않게 스크립을 유지했습니다.
필터링 되는 게시글은 안보이게 되지만, 게시글 분리줄은 유지됩니다.
다른 회색줄과 거의 겹쳐져, 회색 분리줄이 더 찐하게 보이며, 어떤 위치의 게시글을 안 보이게 배제했는지 사용자가 알 수 있습니다.
참고로, 특정 서브 게시판이 아닌 s/all 로 게시글을 보면, 게시글에서 탭 줄이 서브로 보여집니다.
하므로 s/all 에선 탭이 아닌 서브로 필터링을 합니다.
s/all 서브+탭 필터는 불가하다는 뜻이기도 합니다.
// ==UserScript==
// @name Kone 게시글 필터링 개선2
// @namespace http://kone.gg/
// @version 1.2
// @description 제목, 탭 이름, 유저명을 RegExp를 써서 필터링합니다.
// @author 공승아
// @match https://kone.gg/*
// @grant none
// ==/UserScript==
// 스크립 기초
// /스크립에서_이_줄_안읽음/, // 스크립 앞에 '//' 넣으면 줄을 스크립에서 안 읽음
// 스크립 기초
/*
스크립에서_이_줄들_안읽음
스크립에서안읽음이거
여러줄 안 읽게 하기
*/
(function () {
'use strict';
// 제목 필터 패턴 (RegExp)
const titlePatterns = [
// '청아' 단어가 포함된 게시글을 배제합니다
/청아/,
/* RegExp 패턴 예시:
/^테스트1$/, // '테스트1'란 정확한 제목
/테스트2/, // '테스트2'를 포함한 제목
/^테스트3/, // '테스트3'로 시작하는 제목
/테스트4$/, // '테스트4'로 끝나는 제목
/테스트5|테스트6/, // '테스트5' 아니면 '테스트6'가 포함된 제목
/^테스트\d+$/, // 테스트#숫자 예시: 테스트12345
/테.*트 // '테' 와 '트', 그리고 가운데 아무 문자가 들어간게 포함된 제목
/테.*트.*다 //'테' 와 '트', 그리고 가운데 아무 문자가 들어간게 포함된, 또한 '트' 와 '다' 가운데 아무 문자가 들어간 제목
/tEsT/i, // 대문자 소문자 무분별
//기초는 여기까지. 어렵지 않다. 검색하면 다 나와 어떻게 하는지.
*/
];
// 탭 이름 필터 패턴 (RegExp)
const blockedTabPatterns = [
// 'fetishclub' 단어가 포함된 서브를 배제합니다
/fetishclub/,
];
// 작성자 이름 필터 패턴 (RegExp)
const blockedUserPatterns = [
// /도배왕/,
// /스팸봇/,
];
const matchesAny = (text, patterns) =>
patterns.some(pattern => pattern.test(text));
function filterSpamPosts() {
const postAnchors = document.querySelectorAll('a[title]');
postAnchors.forEach(anchor => {
if (anchor.dataset.filtered) return;
const title = anchor.getAttribute('title') || '';
const outerDiv = anchor.closest('div');
if (!outerDiv) return;
const tabNameEl = outerDiv.querySelector('.col-span-2 > div');
const tabName = tabNameEl?.innerText.trim() || '';
const userNameEl = outerDiv.querySelector('#writer_link');
const userName = userNameEl?.innerText.trim() || '';
const shouldHide =
matchesAny(title, titlePatterns) ||
matchesAny(tabName, blockedTabPatterns) ||
matchesAny(userName, blockedUserPatterns);
if (shouldHide) {
console.log('[필터링됨]', { title, tabName, userName });
anchor.style.display = 'none';
anchor.dataset.filtered = 'true';
}
});
}
function observeURLChanges(callback) {
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
const fireCallback = () => setTimeout(callback, 100);
history.pushState = function (...args) {
originalPushState.apply(this, args);
fireCallback();
};
history.replaceState = function (...args) {
originalReplaceState.apply(this, args);
fireCallback();
};
window.addEventListener('popstate', fireCallback);
}
function init() {
filterSpamPosts();
const observer = new MutationObserver(() => filterSpamPosts());
observer.observe(document.body, { childList: true, subtree: true });
observeURLChanges(() => {
console.log('[URL 변경 감지] 게시글 필터링 재실행');
filterSpamPosts();
});
let runCount = 0;
const intervalId = setInterval(() => {
if (runCount++ >= 3) return clearInterval(intervalId);
filterSpamPosts();
}, 1000);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
