Chzzk_L&V: Chatting Plus

"파트너이며 매니저가 아닌 유저" 또는 지정한 streamer 닉네임의 메시지를 연두색으로 표시, 채팅 닉네임 꾸미기 효과 중 스텔스모드 무력화 및 형광펜 제거, 긴 닉네임 10자 초과 시 생략 처리, 타임머신 기능 안내 및 치트키 구매 팝업 클릭하여 닫기

התקן את הסקריפט?
סקריפטים מומלצים של יוצר זה

אולי תאהב גם את Chzzk Auto High Quality.

התקן את הסקריפט
// ==UserScript==
// @name         Chzzk_L&V: Chatting Plus
// @namespace    Chzzk_Live&VOD: Chatting Plus
// @version      1.6
// @description  "파트너이며 매니저가 아닌 유저" 또는 지정한 streamer 닉네임의 메시지를 연두색으로 표시, 채팅 닉네임 꾸미기 효과 중 스텔스모드 무력화 및 형광펜 제거, 긴 닉네임 10자 초과 시 생략 처리, 타임머신 기능 안내 및 치트키 구매 팝업 클릭하여 닫기
// @author       DOGJIP
// @match        https://chzzk.naver.com/*
// @grant        none
// @run-at       document-end
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=chzzk.naver.com
// ==/UserScript==

(function() {
    'use strict';

    const LIGHT_GREEN = "rgb(102,200,102)";

    // ===== 사용자 지정 스트리머 리스트 (파트너가 아닌 스트리머의 이름을 아래의 예시처럼 정확히 넣기) =====
    let streamer = ['고수달', '백곰파', '하나노미냐', '뇨롱이', '새 담', '청 묘', '뱅배준식', '달콤레나 씨', '침착맨', '독케익'];

    let chatObserver = null;

    // 유틸: 닉네임 색상이 너무 어두운 경우 스타일 제거
    function fixUnreadableNicknameColor(nicknameElem) {
        if (!nicknameElem) return;
        const computedColor = window.getComputedStyle(nicknameElem).color;
        const rgbaMatch = computedColor.match(/rgba?\((\d+), ?(\d+), ?(\d+)(?:, ?([0-9.]+))?\)/);
        if (!rgbaMatch) return;

        const r = parseInt(rgbaMatch[1], 10);
        const g = parseInt(rgbaMatch[2], 10);
        const b = parseInt(rgbaMatch[3], 10);
        const a = rgbaMatch[4] !== undefined ? parseFloat(rgbaMatch[4]) : 1;
        const brightness = (r * 299 + g * 587 + b * 114) / 1000;
        const visibility = brightness * a;

        if (visibility < 50) {
            nicknameElem.style.color = '';
        }
    }

    // 유틸: 닉네임 백그라운드 색상 제거
    function removeBackgroundColor(nicknameElem) {
        if (!nicknameElem) return;
        const bgTarget = nicknameElem.querySelector('[style*="background-color"]');
        if (bgTarget) {
            bgTarget.style.removeProperty('background-color');
        }
    }

    // 유틸: 닉네임이 너무 길면 자르고 ... 추가
    function truncateNickname(nicknameElem, maxLen = 10) {
        if (!nicknameElem) return;
        const textSpan = nicknameElem.querySelector('.name_text__yQG50');
        if (!textSpan) return;
        const fullText = textSpan.textContent;
        if (fullText.length > maxLen) {
            textSpan.textContent = fullText.slice(0, maxLen) + '...';
        }
    }

    // 치트키 구매시 타임머신 사용가능 툴팁의 닫기 버튼을 클릭하는 함수
    function autoClickTooltipCloseButton() {
        const closeButton = document.querySelector('.cheat_key_tooltip_button_close__QrFhG');
        if (closeButton) {
            closeButton.click();
        }
    }

    // 채팅 메시지 처리 함수
    function processChatMessage(messageElem) {
        if (messageElem.getAttribute('data-partner-processed') === 'true') return;

        // 파트너 판별
        const isPartner = messageElem.querySelector('[class*="name_icon__zdbVH"]') !== null;

        // 매니저·스트리머 검사 (더 안정적인 단일 이미지 선택 방식)
        const badgeImg = messageElem.querySelector('.badge_container__a64XB img[src*="manager.png"], .badge_container__a64XB img[src*="streamer.png"]');
        const isManager = badgeImg?.src.includes('manager.png');
        const isStreamer = badgeImg?.src.includes('streamer.png');

        const nicknameElem = messageElem.querySelector('.live_chatting_username_nickname__dDbbj');
        const textElem = messageElem.querySelector('.live_chatting_message_text__DyleH');

        // 유틸 함수 처리: 닉네임 가독성, 형광펜 제거, 길이 자르기
        fixUnreadableNicknameColor(nicknameElem);
        removeBackgroundColor(nicknameElem);
        truncateNickname(nicknameElem, 10);

        // 지정 스트리머인지 확인
        const nameSpan = nicknameElem?.querySelector('.name_text__yQG50');
        const nameText = nameSpan ? nameSpan.textContent.trim() : '';
        const isManualStreamer = streamer.includes(nameText);

        // 연두색 적용 조건:
        // (1) 파트너이며 매니저·스트리머가 아닐 때
        // OR
        // (2) 지정한 닉네임이면서 매니저·스트리머가 아닐 때
        if ((!isManager && !isStreamer) && (isPartner || isManualStreamer)) {
            if (nicknameElem) {
                nicknameElem.style.color = LIGHT_GREEN;
                nicknameElem.style.fontWeight = 'bold';
                nicknameElem.style.textTransform = 'uppercase';
            }
            if (textElem) {
                textElem.style.color = LIGHT_GREEN;
                textElem.style.fontWeight = 'bold';
                textElem.style.textTransform = 'uppercase';
            }
        }

        messageElem.setAttribute('data-partner-processed', 'true');
    }

    // 치트키 팝업 감지 및 자동 닫기
    function setupTooltipObserver() {
        const tooltipObserver = new MutationObserver(autoClickTooltipCloseButton);
        const tooltipContainer = document.querySelector('.tooltip');
        if (tooltipContainer) {
            tooltipObserver.observe(tooltipContainer, { childList: true, subtree: true });
        }
    }

    // 채팅 MutationObserver 설정
    function setupChatObserver() {
        if (chatObserver) chatObserver.disconnect();
        const chatContainer = document.querySelector('[class*="live_chatting_list_wrapper__"], [class*="vod_chatting_list__"]');
        if (!chatContainer) {
            setTimeout(setupChatObserver, 500);
            return;
        }

        // 기존 채팅 메시지도 스캔하여 처리
        chatContainer.querySelectorAll('[class^="live_chatting_message_chatting_message__"]')
            .forEach(processChatMessage);

        // 새로운 채팅 메시지 감지
        chatObserver = new MutationObserver(mutations => {
            mutations.forEach(m => {
                m.addedNodes.forEach(node => {
                    if (node.nodeType !== 1) return;
                    if (node.className.includes('live_chatting_message_chatting_message__')) {
                        processChatMessage(node);
                    } else {
                        node.querySelectorAll?.('[class^="live_chatting_message_chatting_message__"]')
                            .forEach(processChatMessage);
                    }
                });
            });
        });
        chatObserver.observe(chatContainer, { childList: true, subtree: false });
    }

    // SPA 내 페이지 전환 감지 및 재설정
    function setupSPADetection() {
        let lastUrl = location.href;
        const onUrlChange = () => {
            if (location.href !== lastUrl) {
                lastUrl = location.href;
                setTimeout(setupChatObserver, 1000);
            }
        };
        ['pushState','replaceState'].forEach(method => {
            const orig = history[method];
            history[method] = function(...args) {
                orig.apply(this, args);
                onUrlChange();
            };
        });
        window.addEventListener("popstate", onUrlChange);
    }

    // 초기화 함수
    function init() {
        setupChatObserver();
        setupSPADetection();
        setupTooltipObserver();
    }

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