// ==UserScript==
// @name Chzzk_L&V: UserBlockPopup
// @namespace Chzzk_L&V: UserBlockPopup
// @version 1.0
// @description 치지직 페이지에서 닉네임 클릭 시 userId 추출 후 차단 팝업 띄우기 (토글 기능 포함)
// @author DOGJIP
// @match https://chzzk.naver.com/*
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
// 1) 스크립트 활성화 여부 플래그
let enabled = true;
// 2) 메뉴에 등록할 명령어 텍스트 업데이트 함수
function updateMenu() {
// 기존 메뉴 제거
try {
GM_unregisterMenuCommand(toggleCommandId);
} catch (e) {
// 첫 실행 시 GM_unregisterMenuCommand가 없을 수 있음. 무시.
}
// 새로운 메뉴 등록
const title = enabled ? '스크립트 비활성화' : '스크립트 활성화';
toggleCommandId = GM_registerMenuCommand(title, () => {
enabled = !enabled;
alert(enabled ? 'UserBlockPopup 스크립트가 활성화되었습니다.' : 'UserBlockPopup 스크립트가 비활성화되었습니다.');
updateMenu();
});
}
let toggleCommandId = null;
updateMenu();
// 3) 원본 pushState 저장
const originalPushState = history.pushState;
// 4) pushState 훅킹
history.pushState = function (...args) {
if (!enabled) {
// 비활성화 상태면 그냥 원래 동작
return originalPushState.apply(this, args);
}
const url = args[2];
if (typeof url === 'string' && /^\/[a-z0-9]{32}$/i.test(url)) {
const userId = url.slice(1);
const userUrl = window.location.origin + '/' + userId;
console.log('[Chzzk-Block] pushState 감지, userId:', userId);
showPopup(userId, userUrl);
// 이동 자체를 막고 싶지 않으면 아래 주석 해제
// return originalPushState.apply(this, args);
return;
}
return originalPushState.apply(this, args);
};
// 5) popstate 이벤트 훅
window.addEventListener('popstate', (e) => {
if (!enabled) return;
const path = location.pathname;
if (/^\/[a-z0-9]{32}$/i.test(path)) {
const userId = path.slice(1);
const userUrl = window.location.origin + '/' + userId;
console.log('[Chzzk-Block] popstate 감지, userId:', userId);
showPopup(userId, userUrl);
}
});
// 6) 클릭 훅
document.body.addEventListener('click', (e) => {
if (!enabled) return;
const span = e.target.closest('span[class^="name_text__"]');
if (!span) return;
const link = span.closest('a[href^="/"]');
if (!link) return;
const href = link.getAttribute('href');
const match = href.match(/^\/([a-z0-9]{32})$/i);
if (!match) return;
const userId = match[1];
const userUrl = window.location.origin + '/' + userId;
console.log('[Chzzk-Block] 클릭 감지, userId:', userId);
e.preventDefault();
e.stopPropagation();
showPopup(userId, userUrl);
});
// 7) 팝업 생성 함수
function showPopup(userId, userUrl) {
// 이미 팝업이 있으면 제거
const existing = document.getElementById('chzzkBlockPopup');
if (existing) existing.remove();
// 팝업 DIV 생성
const popup = document.createElement('div');
popup.id = 'chzzkBlockPopup';
popup.style.cssText = `
position: fixed;
top: 35%;
left: 50%;
transform: translate(-50%, -35%);
background: #fff;
border: 2px solid #333;
padding: 18px 24px;
z-index: 10000;
font-family: Arial, sans-serif;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0,0,0,0.25);
min-width: 300px;
text-align: center;
`;
popup.innerHTML = `
"${userId}" 님을 차단하시겠습니까?
`;
document.body.appendChild(popup);
// “예” 버튼 클릭 시 API 호출 후 팝업 제거
popup.querySelector('#chzzkBlockYes').onclick = async () => {
try {
const res = await fetch(
`https://comm-api.game.naver.com/nng_main/v1/privateUserBlocks/${userId}?loungeId=`,
{
method: 'POST',
credentials: 'include',
headers: {
accept: 'application/json, text/plain, */*',
origin: 'https://game.naver.com',
referer: `https://game.naver.com/profile/${userId}`,
},
}
);
if (res.ok) {
alert(`"${userId}" 님이 차단되었습니다.`);
} else {
alert(`차단 실패 (HTTP ${res.status})`);
}
} catch (err) {
console.error('[Chzzk-Block] 차단 중 오류:', err);
alert('차단 중 오류가 발생했습니다.');
} finally {
popup.remove();
}
};
// “사용자 페이지 이동” 버튼 클릭 시 원래 페이지로 이동
popup.querySelector('#chzzkBlockGo').onclick = () => {
popup.remove();
window.location.href = userUrl;
};
// “닫기” 버튼 클릭 시 팝업만 닫기
popup.querySelector('#chzzkBlockClose').onclick = () => {
popup.remove();
};
}
})();