Chzzk_L&V: UserBlockPopup

치지직 페이지에서 닉네임 클릭 시 userId 추출 후 차단 팝업 띄우기 (토글 기능 포함, 상태 저장)

// ==UserScript==
// @name         Chzzk_L&V: UserBlockPopup
// @namespace    Chzzk_L&V: UserBlockPopup
// @version      1.1
// @description  치지직 페이지에서 닉네임 클릭 시 userId 추출 후 차단 팝업 띄우기 (토글 기능 포함, 상태 저장)
// @author       DOGJIP
// @match        https://chzzk.naver.com/*
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @icon         https://www.google.com/s2/favicons?sz=64&domain=chzzk.naver.com
// ==/UserScript==

(function () {
    'use strict';

    let enabled = false;
    let toggleCommandId = null;

    async function init() {
        enabled = await GM_getValue('chzzkUserBlock_enabled', false);
        updateMenu();
    }

    function updateMenu() {
        try {
            GM_unregisterMenuCommand(toggleCommandId);
        } catch (e) {}

        const title = enabled ? '스크립트 비활성화' : '스크립트 활성화';
        toggleCommandId = GM_registerMenuCommand(title, async () => {
            enabled = !enabled;
            await GM_setValue('chzzkUserBlock_enabled', enabled);
            alert(enabled ? 'UserBlockPopup 스크립트가 활성화되었습니다.' : 'UserBlockPopup 스크립트가 비활성화되었습니다.');
            updateMenu();
        });
    }

    init();

    const originalPushState = history.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;
        }
        return originalPushState.apply(this, args);
    };

    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);
        }
    });

    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] <a> 클릭 감지, userId:', userId);

        e.preventDefault();
        e.stopPropagation();
        showPopup(userId, userUrl);
    });

    function showPopup(userId, userUrl) {
        const existing = document.getElementById('chzzkBlockPopup');
        if (existing) existing.remove();

        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 = `
            <p style="margin-bottom: 14px; font-size: 15px; color: #333;">
                "${userId}" 님을 차단하시겠습니까?
            </p>
            <div style="display: flex; justify-content: center; gap: 8px;">
                <button id="chzzkBlockYes" style="padding: 6px 12px; border:none; background:#d33; color:#fff; border-radius:4px; cursor:pointer;">
                    예
                </button>
                <button id="chzzkBlockGo" style="padding: 6px 12px; border:none; background:#555; color:#fff; border-radius:4px; cursor:pointer;">
                    사용자 페이지 이동
                </button>
                <button id="chzzkBlockClose" style="padding: 6px 12px; border:none; background:#999; color:#fff; border-radius:4px; cursor:pointer;">
                    닫기
                </button>
            </div>
        `;

        document.body.appendChild(popup);

        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();
        };
    }
})();