Greasy Fork is available in English.

hsptv_forum_mute-plus

HSPTV!掲示板のミュート機能です

// ==UserScript==
// @name         hsptv_forum_mute-plus
// @namespace    https://github.com/ze-n-a
// @version      1.1.2
// @description  HSPTV!掲示板のミュート機能です
// @author       ze-n-a
// @match        https://hsp.tv/play/pforum.php*
// @icon         https://hsp.tv/favicon.ico
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';
    const defaultMuteList = `ユーザ名1
ユーザ名2
ユーザ名3`;
    let muteUserList = GM_getValue('muteUserList', defaultMuteList)
        .split(/\r?\n/)
        .map(pattern => pattern.trim())
        .filter(pattern => pattern.length > 0)
        .map(pattern => new RegExp(pattern));
    let isMuteDisabled = GM_getValue('isMuteDisabled', false);
    function saveMuteList(newList, muteDisabled) {
        const trimmedList = newList.trim().split(/\r?\n/)
            .map(pattern => pattern.trim())
            .filter(pattern => pattern.length > 0);
        GM_setValue('muteUserList', trimmedList.join('\n'));
        GM_setValue('isMuteDisabled', muteDisabled);
        muteUserList = trimmedList.map(pattern => new RegExp(pattern));
        isMuteDisabled = muteDisabled;
        location.reload();
    }
    function createMuteListUI() {
        const container = document.createElement('div');
        container.id = 'muteListContainer';
        container.style = `
            position: fixed;
            top: 10px;
            right: 10px;
            width: 250px;
            height: 350px;
            background-color: white;
            border: 1px solid gray;
            padding: 10px;
            z-index: 10000;
            display: none;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            border-radius: 10px;
        `;
        const title = document.createElement('h3');
        title.textContent = 'Mute Keywords and Usernames';
        title.style = `
            margin: 0;
            font-size: 14px;
            font-weight: bold;
            text-align: center;
        `;
        const label = document.createElement('p');
        label.textContent = '正規表現で記入して下さい';
        label.style = `
            margin: 5px 0;
            font-size: 12px;
            color: gray;
        `;
        const textarea = document.createElement('textarea');
        textarea.style = `
            width: 100%;
            height: 200px;
            border: 1px solid gray;
            border-radius: 5px;
            resize: none;
        `;
        textarea.value = GM_getValue('muteUserList', defaultMuteList);
        const checkboxLabel = document.createElement('label');
        checkboxLabel.style = `
            display: block;
            margin-top: 10px;
            font-size: 14px;
        `;
        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.checked = isMuteDisabled;
        checkbox.style.marginRight = '5px';
        checkboxLabel.appendChild(checkbox);
        checkboxLabel.appendChild(document.createTextNode('キーワードミュートをしない'));
        const saveButton = document.createElement('button');
        saveButton.textContent = '保存';
        saveButton.style.margin = '5px';
        saveButton.onclick = () => saveMuteList(textarea.value, checkbox.checked);
        const closeButton = document.createElement('button');
        closeButton.textContent = '閉じる';
        closeButton.style.margin = '5px';
        closeButton.onclick = () => {
            container.style.display = 'none';
        };
        container.appendChild(title);
        container.appendChild(label);
        container.appendChild(textarea);
        container.appendChild(checkboxLabel);
        container.appendChild(saveButton);
        container.appendChild(closeButton);
        document.body.appendChild(container);
    }
    function createToggleButton() {
        const button = document.createElement('div');
        button.id = 'muteToggleButton';
        button.style = `
            position: fixed;
            bottom: 20px;
            right: 20px;
            width: 50px;
            height: 50px;
            background-color: #60aaa0;
            color: white;
            border-radius: 50%;
            text-align: center;
            line-height: 50px;
            cursor: pointer;
            z-index: 10000;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
        `;
        button.textContent = 'M';
        button.onclick = () => {
            const container = document.getElementById('muteListContainer');
            container.style.display = container.style.display === 'none' ? 'block' : 'none';
        };
        document.body.appendChild(button);
    }
    createMuteListUI();
    createToggleButton();
    function replaceMutedUserNames() {
        const currentURL = window.location.href;
        const isTargetURL = /^https:\/\/hsp\.tv\/play\/pforum\.php(\?|$)/.test(currentURL) &&
                            (!currentURL.includes('mode=') ||
                             currentURL.includes('mode=all') ||
                             currentURL.includes('mode=pastwch'));
        if (!isTargetURL) return;
        const tables = document.querySelectorAll('table[width="800"]');
        if (tables.length < 3) return;
        const targetTable = tables[2];
        const userElements = targetTable.querySelectorAll('.date + .text');
        userElements.forEach(element => {
            const userName = element.textContent.trim();
            if (muteUserList.some(regex => regex.test(userName))) {
                element.textContent = '[ミュート]';
            }
        });
    }
    function replaceMutedUserNames2() {
        const currentURL = window.location.href;
        const isTargetURL = /^https:\/\/hsp\.tv\/play\/pforum\.php\?mode=/.test(currentURL);
        if (!isTargetURL) return;
        const mainTable = document.querySelectorAll('table[width="800"]')[2];
        if (!mainTable) return;
        const articles = mainTable.querySelectorAll('table[width="756"]');
        articles.forEach(article => {
            const userInfo = article.querySelector('#info table[width="130"] td p');
            if (!userInfo) return;
            const username = userInfo.textContent.trim();
            const isMutedUser = muteUserList.some(regex => regex.test(username));
            if (isMutedUser) {
                const infoDiv = article.querySelector('#info');
                if (infoDiv) {
                    infoDiv.remove();
                }
                const articleContent = article.querySelector('div.main');
                if (articleContent) {
                    articleContent.textContent = 'ミュートユーザの記事です';
                }
            } else {
                if (!isMuteDisabled) {
                    const articleContent = article.querySelector('div.main');
                    if (articleContent) {
                        muteUserList.forEach(pattern => {
                            const regex = new RegExp(pattern.source, 'g');
                            articleContent.innerHTML = articleContent.innerHTML.replace(regex, '[ミュート]');
                        });
                    }
                }
            }
        });
    }
    replaceMutedUserNames();
    replaceMutedUserNames2();
})();