자동 복호화/국룰입력/다운
피드백이 사라져서 왠지 모르겠는데
일단 기능 추가하는 데에 오래 걸릴거 같아서 코드 합쳐봤음
// ==UserScript==
// @name         Auto somi 개선
// @name:ko      자동 소미 개선
// @namespace    http://tampermonkey.net/
// @description  자동 복호화/국룰입력/다운
// @version      new 6.4.0
// @author       김머시기
// @match        https://kiosk.ac/c/*
// @match        https://kio.ac/c/*
// @match        https://kone.gg/*
// @match        https://arca.live/b/*
// @match        https://mega.nz/*
// @match        https://gofile.io/d/*
// @match        https://workupload.com/*
// @match        https://drive.google.com/file/d/*
// @match        https://drive.google.com/drive/folders/*
// @match        https://drive.usercontent.google.com/download?id*
// @icon         https://lh3.google.com/u/0/d/18OVO7VmnwIuHK6Ke-z7035wKFmMKZ28W=w1854-h959-iv1
// @grant        GM.setValue
// @grant        GM.getValue
// @require      https://openuserjs.org/src/libs/sizzle/GM_config.js
// @grant        GM.registerMenuCommand
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM.xmlHttpRequest
// @grant        GM_xmlhttpRequest
// @license      MIT
// @run-at       document-end
// @downloadURL https://update.greasyfork.org/scripts/496083/Auto%20somi.user.js
// @updateURL https://update.greasyfork.org/scripts/496083/Auto%20somi.meta.js
// ==/UserScript==
'use strict';
let chkp = [,,,, atob('c29taXNvZnQ='), null], Down_Option, PageLoading = [], isT = [,,], MenuID = [null, null, null], host = document.URL.split('/')[2], npw = [], pw = [atob('c29taXNvZnQ='),atob('MjAyNXNvbWlzb2Z0'),
// ================================== Settings ==========================================
// 추가하길 원하는 비밀번호 따옴표 - 쉼표로 구분해서 바로 아래줄에 넣으면 됨 ex) '1234', '2024국룰', '!국룰!'
'atelier', '실루엣21'
];
PageLoading[0] = 1000;
Down_Option = 0;
// =====================================DL관련 설명=================================================
// DlSite Product Information
const allCode = [];
function replaceInvalidCharsForFilename(str) {
    if (str == null) return "";
    return str.replace(/[?\/\\:*"?<>|]/g, match => {
        switch (match) {
            case '?': return '?';
            case '/': return '/';
            case '\\': return '\';
            case ':': return ':';
            case '*': return '*';
            case '"': return '"';
            case '<': return '<';
            case '>': return '>';
            case '|': return '|';
            default: return match;
        }
    });
}
function doFetch(url, options = {
    method: 'GET',
    responseType: 'document'
}, silent = false) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            url: url,
            method: options.method,
            responseType: options.responseType,
            headers: options.headers,
            data: options.data,
            onload: result => {
                console.debug(result)
                if (result.status == 200) {
                    resolve(result.response);
                } else {
                    if (!silent) {
                        console.log(result)
                        alert("알 수 없는 오류로 인해 데이터를 불러오지 못했습니다. " + url)
                        reject(result.status);
                    } else {
                        console.debug(result)
                        reject(result.status);
                    }
                }
            }
        });
    });
}
// 숫자를 바이트 문자열(용량)로 변환합니다
function formatBytes(bytes, decimals = 2) {
    if (bytes === 0) {
        return '-';
    }
    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
// 배열을 문자열로 변환합니다
function arr2str(arr, opt = ", ") {
    let idx = arr.indexOf("")
    while (idx > -1) {
        arr.splice(idx, 1)
        idx = arr.indexOf("")
    }
    return String(arr).replace(/"/g, '').replace('[', "").replace(']', "").replace(/,/g, opt)
}
// DLSite에서 제품 정보를 확인
function getProductInformation(productCode) {
    return new Promise((resolve, reject) => {
        const url = `https://www.dlsite.com/maniax/api/=/product.json?workno=${productCode}&locale=ko-KR`
        doFetch(url, {
                method: 'GET',
                responseType: 'application/json'
            })
            .then(result => {
                const json = JSON.parse(result)[0];
                if (json) {
                    const processedJson = Object()
                    // 특수문자 대체
                    const safeCircleUrl = replaceInvalidCharsForFilename(json.maker_name);
                    const safeTitle = replaceInvalidCharsForFilename(json.work_name);
                    processedJson.thumbnailImage = [``]
                    processedJson.workType = [`${json.work_type} / ${json.work_type_string}`]
                    // processedJson.title = [`${json.work_name}`]
                    // processedJson.title = `${safeTitle}`;
                    processedJson.title = safeTitle;
                    processedJson.intro = [json.intro_s]
                    // processedJson.maker = safeCircleUrl
                    if (json.genres) {
                        processedJson.genres = []
                        json.genres.forEach(genre => {
                            processedJson.genres.push(genre.name)
                        })
                        processedJson.genres = [arr2str(processedJson.genres)]
                    }
                    processedJson.fileInfo = [`${json.file_type}(${formatBytes(json.contents_file_size)})`]
                    // processedJson.workUrl = [`${productCode}`]
                    processedJson.workUrl_button = `https://www.dlsite.com/home/work/=/product_id/${productCode}.html`;
                    processedJson.workUrl = productCode
                    // processedJson.circleUrl = [`${processedJson.maker}`]
                    // processedJson.circleUrl = `${safeCircleUrl}`;
                    processedJson.circleUrl_button = `https://www.dlsite.com/home/circle/profile/=/maker_id/${json.circle_id}.html`;
                    processedJson.circleUrl = safeCircleUrl;
                    resolve(processedJson);
                } else {
                    reject(new Error("Request is failed."));
                }
            })
    });
}
//function createProductElement(productCode) {
window.createProductElement = function (productCode) {
    return new Promise((resolve, reject) => {
        getProductInformation(productCode).then(function(data) {
            const tempDiv = document.createElement('div');
            tempDiv.innerHTML = data.thumbnailImage;
            const imgUrl = tempDiv.querySelector('img')?.src || '';
            const dataFullName =`[${data.workUrl}][${data.circleUrl}] |${data.title}`
            const html =
                '' +
                '' +
                '' +
                `` +
                '' +
                '' +
                '' +
                '
| ' + ' ' + ' +` ${data.workUrl} ${data.workType} ${data.title} ` ` ` ` ` `` + `` + ' '  | 
';
            // ✅ DOMParser로 파싱 (비동기로 쓸 때만)
            //const parser = new DOMParser();
            //const doc = parser.parseFromString(html, 'text/html');
            //const wrapper = doc.body;
            //console.log(wrapper.querySelector('.intro-row'))
            const wrapper = document.createElement('div');
            wrapper.innerHTML = html;
            // 🔹 복사 가능 항목 지정
            makeCopyable(wrapper.querySelector('.title-row'), data.title);
            makeCopyable(wrapper.querySelector('.workUrl-row'), data.workUrl);
            makeCopyable(wrapper.querySelector('.workType-row'), data.workType);
            makeCopyable(wrapper.querySelector('.intro-row'), data.intro);
            makeCopyable(wrapper.querySelector('.circle-row'), data.circleUrl);
            makeCopyable(wrapper.querySelector('.genres-row'), data.genres);
            makeCopyable(wrapper.querySelector('.file-row'), data.fileInfo);
            makeCopyable(wrapper.querySelector('.full-row'), dataFullName);
            // 🔹 RJ/RG 버튼 추가
            //const parentDiv = wrapper.querySelector('td > div'); // td 안의 최상위 div
            const linkDiv = wrapper.querySelector('.link-buttons');
            //if (parentDiv) {
              //parentDiv.style.position = 'relative';
            //}
            if (linkDiv) {
              linkDiv.style.position = 'absolute';
              linkDiv.style.top = '0px';
              linkDiv.style.right = '4px';
              linkDiv.style.display = 'flex';
              linkDiv.style.gap = '0px'; // 버튼 사이 간격
                if (data.workUrl_button) {
                    const rjBtn = document.createElement('a');
                    rjBtn.href = data.workUrl_button;
                    rjBtn.target = '_blank';
                    rjBtn.textContent = 'RJ';
                    rjBtn.style.marginRight = '6px';
                    rjBtn.style.fontWeight = 'bold';
                    linkDiv.appendChild(rjBtn);
                }
                if (data.circleUrl_button) {
                    const rgBtn = document.createElement('a');
                    rgBtn.href = data.circleUrl_button;
                    rgBtn.target = '_blank';
                    rgBtn.textContent = 'RG';
                    rgBtn.style.fontWeight = 'bold';
                    linkDiv.appendChild(rgBtn);
                }
            }
            // resolve(html);
            // resolve(wrapper.innerHTML); // HTML 문자열로 반환
            resolve(wrapper);
        }).catch(reject);
    });
}
// 다크/라이트 모드 갱신 함수
function updateBlockTheme() {
    const isDarkMode = document.documentElement.classList.contains('dark');
    document.querySelectorAll('.my-inserted-block').forEach(el => {
        el.style.backgroundColor = isDarkMode ? "#2c2c2c" : "#f8f9fa";
        el.style.border = isDarkMode ? "1px solid #555" : "1px solid #dee2e6";
    });
}
// 새로운 코드가 들어올 때마다 처리하는 함수
function addCode(code) {
    if (!allCode.includes(code)) allCode.push(code);
    else return;
    const articleView = document.querySelector(".overflow-hidden.break-all");
    if (!articleView) return;
    const articleWrapper = document.querySelector(".pb-4.pt-2.pl-6.pr-3");
    if (!articleWrapper) return;
    // 중복 삽입 방지
    if (!document.querySelector(".my-inserted-header")) {
      // 헤더 추가
      const header = document.createElement('p');
      header.className = "my-inserted-header";
      header.innerHTML = '📦 게시글에서 언급된 게임 목록';
      articleView.insertBefore(header, articleWrapper);
    }
    // 바로 처리
    createProductElement(code).then(html => {
        const contents = document.createElement('div');
        contents.className = "my-inserted-block";
        contents.style.width = "90%";
        contents.style.marginLeft = "auto";
        contents.style.marginRight = "auto";
        contents.style.paddingRight = "10px";
        contents.style.position = "relative";
        //contents.innerHTML = html;
        contents.appendChild(html);
        articleView.insertBefore(contents, articleWrapper);
        updateBlockTheme(); // 새 블록 추가 후 즉시 다크/라이트 적용
    });
}
// 다크모드/라이트모드 감지
if (!window._myInsertedBlockObserver) {
    window._myInsertedBlockObserver = new MutationObserver(updateBlockTheme);
    window._myInsertedBlockObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
}
// =======================================복붙관련===========================================
    // ====== 클릭 시 복사 함수 ======
    function makeCopyable(element, text) {
        if (!element) return;
        element.style.cursor = 'pointer';
        element.onclick = () => {
            navigator.clipboard.writeText(text).then(() => {
                showToast('복사 완료!');
            }).catch(err => {
                console.error('복사 실패:', err);
                showToast('복사 실패');
            });
        };
    }
    // ====== 토스트 표시 함수 ======
    function showToast(message) {
        const toast = document.createElement('div');
        toast.textContent = message;
        toast.style.position = 'fixed';
        toast.style.bottom = '20px';
        toast.style.left = '50%';
        toast.style.transform = 'translateX(-50%)';
        toast.style.background = 'rgba(0,0,0,0.8)';
        toast.style.color = 'white';
        toast.style.padding = '8px 16px';
        toast.style.borderRadius = '8px';
        toast.style.fontSize = '0.9em';
        toast.style.zIndex = '9999';
        toast.style.opacity = '0';
        toast.style.transition = 'opacity 0.3s ease';
        document.body.appendChild(toast);
        requestAnimationFrame(() => toast.style.opacity = '1');
        setTimeout(() => {
            toast.style.opacity = '0';
            toast.addEventListener('transitionend', () => toast.remove());
        }, 1500);
    }
    // ====== RJ / RG 버튼 생성 ======
    function createLinkButton(label, url) {
        const a = document.createElement('a');
        a.href = url;
        a.target = '_blank';
        a.textContent = label;
        a.style.marginRight = '4px';
        a.style.textDecoration = 'none';
        a.style.fontWeight = 'bold';
        return a;
    }
// ===================================원본코드들===================================================
const dlsitePreview = {
    element: null,
    images: [],
    currentIndex: 0,
    isShowing: false,
    activeKeyListener: null,
    activeWheelListener: null, // 마우스 휠 리스너 저장용
    globalKeydownListener: null, // 유지 (document 레벨 키 이벤트)
    isEnabled: true
};
function getKoneGGContentElement() {
    if (host !== 'kone.gg') return null;
    const proseContainer = document.querySelector('div.prose-container');
    if (!proseContainer || !proseContainer.shadowRoot) return null;
    const contentDiv = proseContainer.shadowRoot.querySelector('div.contents');
    return contentDiv;
}
async function handleBlockingModals(currentHost) {
    function hideElement(selector) {
        try {
            const elements = document.querySelectorAll(selector);
            if (elements.length > 0) {
                elements.forEach(el => {
                    if (el.offsetParent !== null) {
                        el.style.setProperty('display', 'none', 'important');
                    }
                });
            }
        } catch (e) {
        }
    }
    if (currentHost === 'kone.gg') {
        const nsfwOverlayContainer = document.querySelector('div.relative.min-h-60 > div.absolute.w-full.h-full.backdrop-blur-2xl');
        if (nsfwOverlayContainer && nsfwOverlayContainer.offsetParent !== null) {
            const viewContentButton = nsfwOverlayContainer.querySelector('div.flex.gap-4 button:nth-child(2)');
            if (viewContentButton && viewContentButton.textContent?.includes('콘텐츠 보기')) {
                viewContentButton.click();
                await new Promise(resolve => setTimeout(resolve, 500));
            } else {
                const modalSelectorsKone = [
                    '.age-verification-popup',
                    '.content-overlay.block',
                ];
                modalSelectorsKone.forEach(selector => hideElement(selector));
            }
        }
    } else if (currentHost === 'arca.live') {
        const modalSelectorsArca = [
            { selector: '.adult-confirm-modal', action: 'hide' },
            { selector: '.fc-dialog', action: 'hide' },
            { selector: '#preview-block-layer', action: 'hide' },
            { selector: 'div[class*="adult-channel-confirm"]', action: 'hide' },
            { selector: 'div.modal[data-id="confirmAdult"] div.modal-footer button.btn-primary', action: 'click'},
            { selector: 'button.btn-primary.btn.text-light[data-bs-dismiss="modal"]', action: 'click' }
        ];
        modalSelectorsArca.forEach(item => {
            const elements = document.querySelectorAll(item.selector);
            elements.forEach(element => {
                if (element && element.offsetParent !== null) {
                    if (item.action === 'click') {
                        element.click();
                    } else {
                        hideElement(item.selector);
                    }
                }
            });
        });
    }
}
async function toggleDown(){
    isT[0]=!isT[0];
    if(!isT[0] && isT[1]){
        isT[1]=false;
        await GM.setValue('isT[1]', isT[1]);
    }
    await GM.setValue('isT[0]', isT[0]);
    updateDown();
    updateTab();
    toggleDlsitePreview();
}
async function toggleTab(){
    isT[1]=!isT[1];
    if(!isT[0] && isT[1]){
        isT[0]=true;
        await GM.setValue('isT[0]', isT[0]);
    }
    await GM.setValue('isT[1]', isT[1]);
    updateDown();
    updateTab();
    toggleDlsitePreview();
}
async function toggleDlsitePreview() {
    dlsitePreview.isEnabled = !dlsitePreview.isEnabled;
    await GM.setValue('dlsitePreviewEnabled', dlsitePreview.isEnabled);
    updateDown();
    updateTab();
    updateDlsitePreviewMenu();
    if (!dlsitePreview.isEnabled && dlsitePreview.isShowing) {
        hideDlsitePreview();
    }
    const allDlsiteLinks = document.querySelectorAll('a[href*="dlsite.com"]');
    allDlsiteLinks.forEach(link => {
        link.removeEventListener('mouseenter', showDlsitePreview);
        link.removeEventListener('mouseleave', hideDlsitePreview);
        delete link.dataset._dlsite_preview_hooked;
        if (dlsitePreview.isEnabled) {
            link.addEventListener('mouseenter', showDlsitePreview);
            link.dataset._dlsite_preview_hooked = '1';
        }
    });
}
function updateDown(){
    if(MenuID[0] !==null)GM_unregisterMenuCommand(MenuID[0]);
    MenuID[0]=GM_registerMenuCommand(`자동 다운로드  ${isT[0] ? 'ON' : 'OFF'}`, toggleDown, { autoClose: false, title: `자동 다운로드 ${isT[0] ? '켜짐' : '꺼짐'}`});
}
function updateTab(){
    if(MenuID[1] !==null)GM_unregisterMenuCommand(MenuID[1]);
    MenuID[1]=GM_registerMenuCommand(`자동 탭 닫기    ${isT[1] ? 'ON' : 'OFF'}`, toggleTab, { autoClose: false, title: `자동 탭 닫기 ${isT[1] ? '켜짐' : '꺼짐'}`});
}
function updateDlsitePreviewMenu(){
    if(MenuID[2] !==null)GM_unregisterMenuCommand(MenuID[2]);
    MenuID[2]=GM_registerMenuCommand(`DLsite 미리보기 ${dlsitePreview.isEnabled ? 'ON' : 'OFF'}`, toggleDlsitePreview, { autoClose: false, title: `DLsite 미리보기 ${dlsitePreview.isEnabled ? '켜짐' : '꺼짐'}`});
}
function decodeContent(target, reg) {
    try {
        if (!target || !target.innerHTML) return;
        const originalHTML = target.innerHTML;
        let newHTML = originalHTML;
        const matches = [...originalHTML.matchAll(reg)];
        if (matches.length === 0) return;
        for (const match of matches) {
            let encodedString = match[0];
            let decodedPotentialUrl = encodedString;
            try {
                let previousDecoded = "";
                for (let i = 0; i < 5; i++) {
                    if (!decodedPotentialUrl || typeof decodedPotentialUrl !== 'string') break;
                    let currentDecoded;
                    try {
                        currentDecoded = atob(decodedPotentialUrl);
                    } catch(e) {
                        break;
                    }
                    if (currentDecoded.toLowerCase().startsWith('http://') || currentDecoded.toLowerCase().startsWith('https://')) {
                        decodedPotentialUrl = currentDecoded;
                        break;
                    }
                    if (previousDecoded === currentDecoded) {
                        break;
                    }
                    previousDecoded = currentDecoded;
                    decodedPotentialUrl = currentDecoded;
                }
                if (decodedPotentialUrl && typeof decodedPotentialUrl === 'string' &&
                    (decodedPotentialUrl.toLowerCase().startsWith('http://') || decodedPotentialUrl.toLowerCase().startsWith('https://'))) {
                    try {
                        const parsedUrl = new URL(decodedPotentialUrl);
                        if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
                            continue;
                        }
                        const cleanHref = parsedUrl.href;
                        const textSpan = document.createElement('span');
                        textSpan.textContent = cleanHref;
                        const safeLinkDisplayText = textSpan.innerHTML;
                        const linkHTML = `${safeLinkDisplayText}`;
                        newHTML = newHTML.replace(encodedString, linkHTML);
                    } catch (urlError) {
                    }
                }
            } catch (decodeError) {
            }
        }
        if (target.innerHTML !== newHTML) {
            target.innerHTML = newHTML;
        }
    } catch (e) {
    }
}
function doDec() {
    if (chkp[3] !== chkp[4]) return;
    let targets = [];
    if (host === 'arca.live') {
        targets = [
            document.querySelector('body div.article-body > div.fr-view.article-content'),
            ...document.querySelectorAll('div.article-comment#comment div.comment-content, div.article-comment div.comment-content')
        ].filter(el => el !== null);
    } else if (host === 'kone.gg') {
        const koneContentElement = getKoneGGContentElement();
        const comments = document.querySelectorAll('p.text-sm.whitespace-pre-wrap');
        const listItems = document.querySelectorAll('ol.list-decimal li p');
        targets = [koneContentElement, ...comments, ...listItems].filter(el => el !== null);
    }
    if (targets.length === 0 || (targets.length === 1 && !targets[0])) return;
    for (const target of targets) {
        if (!target) continue;
        const links = target.querySelectorAll('a');
        links.forEach(a => {
            a.setAttribute('rel', 'noreferrer');
        });
        decodeContent(target, /aHR0c[0-9A-Za-z+/=]{8,}/g);
        decodeContent(target, /YUhSMG[0-9A-Za-z+/=]{8,}/g);
        decodeContent(target, /WVVoU[0-9A-Za-z+/=]{8,}/g);
        decodeContent(target, /V1ZWb[0-9A-Za-z+/=]{8,}/g);
        decodeContent(target, /ttps:\/\/[0-9A-Za-z./?=&#%_-]+/g);
        doDlsiteContextAwareForElement(target);
    }
    if (host === 'kone.gg') {
        const koneContentElement = getKoneGGContentElement();
        if (!koneContentElement || koneContentElement.querySelector('.dlsite-link-appended')) return;
        const titleText = document.querySelector('h1.flex, h1.text-xl')?.textContent || '';
        const commentTexts = [...document.querySelectorAll('.text-sm.whitespace-pre-wrap')]
            .map(el => el.textContent || '')
            .join(' ');
        const bodyText = koneContentElement?.textContent || '';
        const allText = `${titleText} ${bodyText} ${commentTexts}`;
        const rjMatches = [...allText.matchAll(/\b(RJ|rj|Rj|rJ)([0-9]{5,10})\b/g)];
        const rjSet = new Set(rjMatches.map(m => m[1].toUpperCase() + m[2]));
        for (const code of rjSet) {
          addCode(code);
        }
        if (rjSet.size === 1) {
            const onlyCode = [...rjSet][0];
            const linkUrl = `https://www.dlsite.com/maniax/work/=/product_id/${onlyCode}.html`;
            const finalLine = document.createElement('div');
            finalLine.className = 'dlsite-link-appended';
            finalLine.style.marginTop = '1em';
            finalLine.innerHTML = `▶ ${onlyCode} DLsite 링크`;
            koneContentElement.appendChild(finalLine);
            const appendedLink = finalLine.querySelector('a[href*="dlsite.com"]');
            if (appendedLink && !appendedLink.dataset._dlsite_preview_hooked) {
                if (dlsitePreview.isEnabled) {
                    appendedLink.addEventListener('mouseenter', showDlsitePreview);
                    appendedLink.addEventListener('mouseleave', hideDlsitePreview, { once: true });
                }
                appendedLink.dataset._dlsite_preview_hooked = '1';
            }
        }
    }
}
function updateDlsitePreviewImage() {
    if (!dlsitePreview.element || dlsitePreview.images.length === 0) {
        return;
    }
    dlsitePreview.element.innerHTML = '';
    const img = document.createElement('img');
    img.src = dlsitePreview.images[dlsitePreview.currentIndex];
    img.referrerPolicy = 'no-referrer';
    img.style.maxWidth = '100%';
    img.style.maxHeight = '100%';
    dlsitePreview.element.appendChild(img);
}
function handleDlsiteKeydown(event) {
    if (!dlsitePreview.element || !dlsitePreview.isShowing || dlsitePreview.images.length <= 1) {
        return;
    }
    if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
        event.stopPropagation();
        event.preventDefault();
        if (event.key === 'ArrowLeft') {
            dlsitePreview.currentIndex = (dlsitePreview.currentIndex - 1 + dlsitePreview.images.length) % dlsitePreview.images.length;
        } else if (event.key === 'ArrowRight') {
            dlsitePreview.currentIndex = (dlsitePreview.currentIndex + 1) % dlsitePreview.images.length;
        }
        updateDlsitePreviewImage();
    }
}
function handleDlsiteMouseWheel(event) {
    if (!dlsitePreview.element || !dlsitePreview.isShowing || dlsitePreview.images.length <= 1) {
        return;
    }
    event.stopPropagation();
    event.preventDefault();
    if (event.deltaY < 0) { // Wheel up
        dlsitePreview.currentIndex = (dlsitePreview.currentIndex - 1 + dlsitePreview.images.length) % dlsitePreview.images.length;
    } else if (event.deltaY > 0) { // Wheel down
        dlsitePreview.currentIndex = (dlsitePreview.currentIndex + 1) % dlsitePreview.images.length;
    }
    updateDlsitePreviewImage();
}
function showDlsitePreview(event) {
    if (!dlsitePreview.isEnabled) return;
    const link = event.target;
    const productUrl = link.href;
    if (dlsitePreview.isShowing) return;
    dlsitePreview.isShowing = true;
    dlsitePreview.element = document.createElement('div');
    dlsitePreview.element.style.cssText = `
        position: fixed;
        z-index: 9999;
        background-color: rgba(0, 0, 0, 0.8);
        border: 1px solid #333;
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
        max-width: 480px;
        max-height: 480px;
        overflow: hidden;
        pointer-events: auto; /* Make sure wheel events are captured by this element */
        display: flex;
        justify-content: center;
        align-items: center;
    `;
    document.body.appendChild(dlsitePreview.element);
    moveDlsitePreview(event);
    document.addEventListener('mousemove', moveDlsitePreview);
    GM.xmlHttpRequest({
        method: "GET",
        url: productUrl,
        onload: function(response) {
            const parser = new DOMParser();
            const doc = parser.parseFromString(response.responseText, "text/html");
            const imgContainer = doc.querySelector('.product-slider');
            if (!imgContainer) {
                if (dlsitePreview.element) {
                    dlsitePreview.element.textContent = '미리보기를 불러올 수 없습니다.';
                    dlsitePreview.element.style.color = '#fff';
                }
                return;
            }
            dlsitePreview.images = [];
            dlsitePreview.currentIndex = 0;
            const imageDataElements = imgContainer.querySelectorAll('.product-slider-data > div[data-src]');
            imageDataElements.forEach(dataEl => {
                const imageUrl = dataEl.dataset.src;
                if (
                    imageUrl &&
                    !imageUrl.includes('data:image') &&
                    !imageUrl.toLowerCase().startsWith('javascript:') &&
                    !imageUrl.includes('/resize/')
                ) {
                    dlsitePreview.images.push(imageUrl);
                }
            });
            if (dlsitePreview.element && dlsitePreview.images.length > 0) {
                updateDlsitePreviewImage();
                if (dlsitePreview.globalKeydownListener) {
                    document.removeEventListener('keydown', dlsitePreview.globalKeydownListener);
                }
                dlsitePreview.globalKeydownListener = handleDlsiteKeydown;
                document.addEventListener('keydown', dlsitePreview.globalKeydownListener);
                if (dlsitePreview.activeWheelListener) {
                    dlsitePreview.element.removeEventListener('wheel', dlsitePreview.activeWheelListener);
                }
                dlsitePreview.activeWheelListener = handleDlsiteMouseWheel;
                dlsitePreview.element.addEventListener('wheel', dlsitePreview.activeWheelListener, { passive: false });
            } else if (dlsitePreview.element) {
                dlsitePreview.element.textContent = '이미지를 찾을 수 없습니다.';
                dlsitePreview.element.style.color = '#fff';
            }
        },
        onerror: function() {
            if (dlsitePreview.element) {
                dlsitePreview.element.textContent = '미리보기 로드 실패';
                dlsitePreview.element.style.color = '#fff';
            }
        }
    });
    link.addEventListener('mouseleave', hideDlsitePreview);
}
function moveDlsitePreview(event) {
    if (dlsitePreview.element) {
        dlsitePreview.element.style.top = `${event.clientY + 15}px`;
        dlsitePreview.element.style.left = `${event.clientX + 15}px`;
    }
}
function hideDlsitePreview() {
    if (dlsitePreview.element) {
        if (dlsitePreview.activeWheelListener) {
            dlsitePreview.element.removeEventListener('wheel', dlsitePreview.activeWheelListener);
            dlsitePreview.activeWheelListener = null;
        }
        document.body.removeChild(dlsitePreview.element);
        dlsitePreview.element = null;
    }
    dlsitePreview.isShowing = false;
    dlsitePreview.images = [];
    dlsitePreview.currentIndex = 0;
    document.removeEventListener('mousemove', moveDlsitePreview);
    if (dlsitePreview.globalKeydownListener) {
        document.removeEventListener('keydown', dlsitePreview.globalKeydownListener);
        dlsitePreview.globalKeydownListener = null;
    }
}
function doDlsiteContextAwareForElement(element) {
    if (!element) return;
    const dlsiteCodePattern = /(?:(RJ|R\s*J|꺼|거|DL|D\s*L)|(VJ|V\s*J|퍼))\s*((?:[\s:()\[\]#.-]*[0-9]){5,10})/gi;
    const textContent = element.textContent || '';
    let tempHTML = element.innerHTML;
    const processedOriginalTexts = new Set();
    let match;
    while ((match = dlsiteCodePattern.exec(textContent)) !== null) {
        const originalText = match[0];
        if (processedOriginalTexts.has(originalText)) {
            continue;
        }
        const rjPrefix = match[1];
        const numbersWithJunk = match[3];
        const prefix = rjPrefix ? 'RJ' : 'VJ';
        const code = numbersWithJunk.replace(/[^0-9]/g, '');
        if (code.length < 5 || code.length > 10) {
            continue;
        }
        const fullCode = `${prefix}${code}`;
        const linkUrl = `https://www.dlsite.com/maniax/work/=/product_id/${fullCode}.html`;
        addCode(fullCode);
        const textSpan = document.createElement('span');
        textSpan.textContent = originalText;
        const safeLinkText = textSpan.innerHTML;
        const escapedOriginalText = originalText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        const replaceRegex = new RegExp(escapedOriginalText, 'g');
        const safeHref = linkUrl.replace(/"/g, '"');
        let replaced = false;
        tempHTML = tempHTML.replace(replaceRegex, (match) => {
            if (replaced) return match;
            const contextBefore = tempHTML.substring(tempHTML.lastIndexOf('<', tempHTML.indexOf(match)), tempHTML.indexOf(match));
            const contextAfter = tempHTML.substring(tempHTML.indexOf(match) + match.length, tempHTML.indexOf('>', tempHTML.indexOf(match) + match.length) + 1);
            if (contextBefore.includes('href=') || contextAfter.includes('${safeLinkText}`;
        });
    }
    if (element.innerHTML !== tempHTML) {
        element.innerHTML = tempHTML;
    }
    const dlsiteLinks = element.querySelectorAll('a[href*="dlsite.com"]');
    dlsiteLinks.forEach(link => {
        const match = link.href.match(/\b(RJ|VJ)(\d{5,10})\b/i);
        if (match) {
          addCode(match[0]); // RJ&VJ번호
        }
        if (!link.dataset._dlsite_preview_hooked) {
            if (dlsitePreview.isEnabled) {
                link.addEventListener('mouseenter', showDlsitePreview);
                link.addEventListener('mouseleave', hideDlsitePreview, { once: true });
            }
            link.dataset._dlsite_preview_hooked = '1';
        }
    });
}
async function chkPW(){
    chkp[3]=await GM.getValue('chkp[3]');
    isT[0]=await GM.getValue('isT[0]', true);
    isT[1]=await GM.getValue('isT[1]', false);
    dlsitePreview.isEnabled = await GM.getValue('dlsitePreviewEnabled', true);
    updateDown();
    updateTab();
    updateDlsitePreviewMenu();
    if(host=='arca.live' || host=='kone.gg'){
        if(chkp[3] !=chkp[4]){
            const chk=prompt('국룰을 입력해주세요');
            if(chk?.toLowerCase()==chkp[4]) {
                await GM.setValue('chkp[3]', chkp[4]);
            } else {
                GM.setValue('chkp[3]', false);
                alert('국룰이 틀렸습니다');
            }
        }
    }
}
async function inputPW() {
    let inputElem = document.querySelector(chkp[0]), btnElem = document.querySelector(chkp[1]);
    if (!inputElem ) {
        if (isT[0] === true && !document.querySelector('.files-list, #download-section')) {
            await new Promise(res => setTimeout(res, PageLoading[1] || 1000)).then(DBtn);
        }
        return;
    }
    const combinedPw = [...new Set([...pw, ...npw])];
    if (chkp[3] == chkp[4]) {
        try {
            for (let i = 0; i < combinedPw.length; i++) {
                if (!combinedPw[i]) continue;
                if (!inputElem) break;
                inputElem.value = combinedPw[i];
                if (host == 'kio.ac') {
                    inputElem.dispatchEvent(new Event('input', { bubbles: true }));
                    inputElem.dispatchEvent(new Event('change', { bubbles: true }));
                    if (btnElem) {
                      let retry = 0;
                      while (btnElem.disabled && retry < 200) {
                        await new Promise(res => setTimeout(res, 50));
                        retry++;
                      }
                      btnElem.click();
                    }
                } else {
                    if(btnElem) btnElem.click();
                    else {
                        const enterEvent = new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true });
                        inputElem.dispatchEvent(enterEvent);
                    }
                }
                await new Promise(res => setTimeout(res, 800));
                const successIndicator = document.querySelector('.files-list, #download-section, .download-link, .btn-download, .main-button-download');
                const errorIndicator = document.querySelector('.text-error, .text-red-500, .error-message, .incorrect-password, [class*="error"], [id*="error"]');
                if (successIndicator && successIndicator.offsetParent !== null) {
                    break;
                } else if (errorIndicator && errorIndicator.offsetParent !== null) {
                    if (inputElem) inputElem.value = '';
                }
            }
            if (isT[0] == true) {
                await new Promise(res => setTimeout(res, PageLoading[1] || 1000)).then(DBtn);
            }
        } catch (e) {
            if (isT[0] == true) {
                await new Promise(res => setTimeout(res, PageLoading[1] || 1000)).then(DBtn);
            }
        }
    }
}
async function kioskdone(){
    try {
        await new Promise(res=> setTimeout(res, 3000));
        for(let i=0, jj=0; jj!=1; i++){
            await new Promise(res=> setTimeout(res, 1000));
            if(document.querySelector('.flex.flex-row.text-xs.justify-between div:nth-child(2)').innerText=='done'){
                await new Promise(res=> setTimeout(res, 2000)).then(() => window.close());
                jj++;
            }
        }
    } catch(e){
        if(isT[1]==true && isT[2]==true) window.close();
    }
}
async function DBtn(){
    if (isT[0] !== true) return;
    if (host === 'kiosk.ac') {
        try {
            const btns = document.querySelectorAll(chkp[2]);
            const clickedSet = new Set();
            for (const btn of btns) {
                if (!btn || btn.offsetParent === null) continue;
                const key = btn.closest('tr')?.innerText?.trim();
                if (clickedSet.has(key)) continue;
                let waitCount = 0;
                while (btn.disabled && waitCount < 10) {
                    await new Promise(res => setTimeout(res, 200));
                    waitCount++;
                }
                if (btn.disabled) continue;
                clickedSet.add(key);
                btn.click();
                await new Promise(res => setTimeout(res, 1800));
            }
        } catch (e) {}
    }
    else if (host === 'mega.nz') {
        try {
            const resumeButton = document.querySelector('.mega-button.positive.resume.js-resume-download');
            if (resumeButton) resumeButton.click();
            const standardDownloadButton = document.querySelector('.mega-button.positive.js-default-download.js-standard-download');
            if (standardDownloadButton) standardDownloadButton.click();
            const continueDownloadButton = document.querySelector('.mega-button.large.positive.download.continue-download');
            if (continueDownloadButton) continueDownloadButton.click();
        } catch (e) {}
    }
    else {
        try {
            const btns = document.querySelectorAll(chkp[2]);
            const clickedSet = new Set();
            for (const btn of btns) {
                if (!btn || btn.offsetParent === null || btn.classList.contains('btn-disabled')) continue;
                const key = btn.closest('tr')?.innerText?.trim();
                if (!clickedSet.has(key)) {
                    clickedSet.add(key);
                    btn.click();
                    await new Promise(res => setTimeout(res, 300));
                }
            }
        } catch (e) {}
    }
    if (isT[1] === true && isT[2] === true) {
        setTimeout(() => {
            if (host === 'kiosk.ac') {
                kioskdone();
            } else {
                window.close();
            }
        }, 1500);
    }
}
async function FindPW(){
    let atc;
    if (host === 'arca.live') {
        atc = document.querySelector('body div.article-body > div.fr-view.article-content');
    } else if (host === 'kone.gg') {
        atc = getKoneGGContentElement();
    }
    if (!atc || !atc.innerHTML) return;
    let tempContent = atc.innerHTML;
    tempContent=tempContent.replace(/ /g, ' ').replace(/( ){2,}/g, ' ');
    tempContent=tempContent.replace(/국룰/g, 'ㄱㄹ');
    let regexx=/(대문자)/;
    if(regexx.test(tempContent)) {
        const smpeopleUpper = atob('U01QRU9QTEU=');
        if (npw.indexOf(smpeopleUpper) === -1) {
            npw.push(smpeopleUpper);
        }
    }
    function processPasswordRegex(reg){
        let currentLoopContent = tempContent;
        const processedTexts = new Set();
        while(true){
            const matchResult = reg.exec(currentLoopContent);
            if (!matchResult) break;
            let matchedText = matchResult[0];
            if (processedTexts.has(matchedText)) {
                currentLoopContent = currentLoopContent.replace(matchedText, `__SKIPPED_${Math.random().toString(36).substring(2, 10)}__`);
                continue;
            }
            let DECed = matchedText.replace(/(ㄱㄹ)/g, '국룰');
            let DECedd = DECed.replace(/\s|[+]|(은|는|이|가)|(전부)|(대문자로)|(대문자)|(비밀번호)|(패스워드)|(비번)|(ㅂㅂ)|(암호)|(ㅇㅎ)|(키오스크맘)|(키오스크)|(입니다)|(이고)|(이며)|(임다)|(같다)|(처럼)|(틀리다)|(입니다요)/g, '');
            DECedd = DECedd.split(/[(입)(임)(이)(이)(이)(입)(임)(같)(처)(틀)]/g)[0];
            DECedd = DECedd.replace(/[<>]/g, '');
            let dat;
            if (host === 'arca.live') {
                const dateEl = document.querySelector('.article-info .date .body');
                if (dateEl && typeof dateEl.innerText === 'string' && dateEl.innerText.trim() !== '') {
                    dat = dateEl.innerText.split(' ')[0];
                } else {
                    dat = undefined;
                }
            }
            let regexa=/(오늘)|(날짜)|(날자)/g;
            if(dat && regexa.test(DECedd)){
                DECedd=DECedd.replace(regexa, '');
                const dateParts = dat.split(/\-/g);
                if (dateParts.length === 3) {
                    const year = dateParts[0].slice(2);
                    const month = dateParts[1];
                    const day = dateParts[2];
                    let dateFormats=[month + day, month + '-' + day, year + month + day, dat.replace(/-/g,'')];
                    for(let k=0;k 2 && npw.indexOf(finalPw)=='-1') {
                            npw.push(finalPw);
                        }
                    }
                }
            } else {
                const finalPw = DECedd.replace(/국룰/g,chkp[4]);
                if(finalPw && finalPw.length > 2 && npw.indexOf(finalPw)=='-1') {
                    npw.push(finalPw);
                }
            }
            processedTexts.add(matchedText);
            currentLoopContent = currentLoopContent.replace(matchedText, `__PROCESSED_${Math.random().toString(36).substring(2, 15)}__`);
        }
    }
    processPasswordRegex(/[(
\s*)*]{0,}[ㄱ-ㅣ가-힣0-9A-Za-z\s~`!^\_+@\#$%&=]{0,}(ㄱㄹ){1,1}[ㄱ-ㅣ가-힣0-9A-Za-z\s~`!^\_+@\#$%&=]{0,}[(\s*
)*]{0,}/);
DLsite링크에 대서 미리보기 볼 필요없이
https://greasyfork.org/ko/scripts/433939-dlsite-product-information-injector/code
위 링크를 참고해서 게임관련 설명이 간략하게 나오게 하면 좋을 것 같은데 어떠신지요?