GM论坛勋章百宝箱

默认关闭回收功能,徽章按类型排序,保存/还原徽章顺序,勋章一键续期,勋章收益统计功能

目前為 2024-09-18 提交的版本,檢視 最新版本

// ==UserScript==
// @name         GM论坛勋章百宝箱
// @namespace    http://tampermonkey.net/
// @version      1.25
// @description  默认关闭回收功能,徽章按类型排序,保存/还原徽章顺序,勋章一键续期,勋章收益统计功能
// @match        https://www.gamemale.com/wodexunzhang-showxunzhang.html?action=my
// @grant        GM_addStyle
// @license MIT
// ==/UserScript==

// TODO所有勋章一键续,剧情勋章一键续期
// 1.25版本测试自动更新情况
(function () {
    'use strict';
    // 徽章按类型排序和顺序调整
    // 如果想改动默认顺序就改这里
    const orderList = ['游戏男从', '真人男从', '女从', '装备', '资产', '宠物', '板块', '天赋', '赠礼', '咒术', '剧情', '其他']

    let categoriesData = {}

    // 从我的日志里,把最新的勋章分类数据拿过来
    fetchBlogArticle().then(articleArray => {
        categoriesData = articleArray;

        // 此处直接复制粘贴代码不想思考了
        let result = { "游戏男从(10)": "", "真人男从(8)": "", "女从(4)": "", "装备(11)": "", "资产(16)": "", "宠物(7)": "", "板块(4)": "", "天赋(4)": "", "赠礼": "", "咒术": "", "剧情": "", "其他": "" };
        let categories = {
            "youxi": "游戏男从(10)", "zhenren": "真人男从(8)", "Maid": "女从(4)",
            "Equip": "装备(11)", "Asset": "资产(16)", "Pet": "宠物(7)",
            "Forum": "板块(4)", "Skill": "天赋(4)", "Gift": "赠礼",
            "Spell": "咒术", "Plot": "剧情"
        };

        let myblok = document.getElementsByClassName("myblok");
        for (let blok of myblok) {
            let regex = /alt="(.+?)"/;
            let matches = blok.innerHTML.match(regex)

            if (matches) {
                let match = matches[1];
                let found = false;

                for (let key in categories) {
                    if (categoriesData[key].indexOf(match) >= 0) {
                        result[categories[key]] += match + ",";
                        found = true;
                        break;
                    }
                }

                if (!found) {
                    result["其他"] += match + ",";
                }
            }
        }
        let txt = ""
        for (let key in result) {
            txt += key + " : (" + (result[key].split(",").length - 1) + ") " + result[key].slice(0, -1) + "<br>"
        }

        function qiwang(pattern) {
            let myblok = document.getElementsByClassName("myblok")
            let result = { "金币": 0, "血液": 0, "咒术": 0, "知识": 0, "旅程": 0, "堕落": 0, "灵魂": 0 };
            for (let blok of myblok) {
                if (blok.innerText.indexOf("已寄售") > 0) {
                    continue
                }
                let regex = /几率 (\d+)%/i;
                let matches = blok.innerText.match(regex)

                // let nameRegex=/\t(.+?)\n/;
                // let nameMatch=blok.innerText.replaceAll(" ","").match(nameRegex)
                if (matches) {
                    let prob = matches[1]
                    let symbols = Array.from(blok.innerText.matchAll(pattern), m => m[2]);
                    let isSame = symbols.every(function (element) {
                        return element === symbols[0];
                    });
                    // console.log(nameMatch[1]+isSame)
                    matches = blok.innerText.matchAll(pattern);
                    for (let match of matches) {
                        let score = prob / 100 * parseInt(match[2] + match[3])
                        result[match[1]] = Number((result[match[1]] + score).toFixed(4));
                    }
                }
            }
            return result
        }

        function getCoin() {
            let coin = 0;
            let myblok = document.getElementsByClassName("myblok")
            for (let blok of myblok) {
                let regex = /金币\s+(\d+)寄售/i;
                let matches = blok.innerText.match(regex)
                if (matches) {
                    coin += parseInt(matches[1])
                }
            }
            return coin
        }

        function showValid() {
            let myblok = document.getElementsByClassName("myblok")
            for (let blok of myblok) {
                let regex = /\s+(.+?分)\d{1,2}秒有效期/i;
                let matches = blok.innerText.match(regex)
                if (matches) {
                    let newP = document.createElement("p");
                    let newContent = document.createTextNode(matches[1]);
                    newP.appendChild(newContent);
                    blok.firstElementChild.appendChild(newP)
                }
            }
        }

        let huiPattern = /回帖\s+(.+?) ([+-])(\d+)/gi
        let huiResult = qiwang(huiPattern)
        let hui = "回帖期望 "
        for (let key in huiResult) {
            hui += key + ":" + huiResult[key] + "&nbsp;&nbsp;"
        }

        let faPattern = /发帖\s+(.+?) ([+-])(\d+)/gi
        let faResult = qiwang(faPattern)
        let fa = "发帖期望 "
        for (let key in faResult) {
            fa += key + ":" + faResult[key] + "&nbsp;&nbsp;"
        }

        let coin = "寄售最大价格总和:" + getCoin()

        // document.head.innerHTML += '<style>.myfldiv {display:flex;flex-wrap:wrap;align-items:flex-start;}</style>';
        // 找到元素并更新其内容
        var badgeOrderElement = document.querySelector(".badge-order");
        if (badgeOrderElement) {
            badgeOrderElement.innerHTML = "<p>" + hui + "<br>" + fa + "<br>" + coin + "<br><br>" + txt +"</p>";
        }

        showValid()
    })


    const linkList = { "游戏男从": "youxi", "真人男从": "zhenren", "女从": "Maid", "装备": "Equip", "资产": "Asset", "宠物": "Pet", "板块": "Forum", "天赋": "Skill", "赠礼": "Gift", "咒术": "Spell", "剧情": "Plot", "其他": "other" }

    // 创建一个新的div元素用于管理徽章
    const badgeManagerDiv = document.createElement('div');
    badgeManagerDiv.className = 'badge-manager';
    badgeManagerDiv.innerHTML = '<h2>徽章管理</h2><p>这里可以管理您的徽章。</p><div class="badge-manager-button"><div>';

    const badgeOrderDiv = document.createElement('div');
    badgeOrderDiv.className = 'badge-order';
    badgeOrderDiv.innerHTML = '正在计算您拥有的徽章类型和价值,请稍等。。。'

    // 获取目标div并在其前面插入新创建的div
    const targetDiv = document.querySelector('.my_fenlei');
    targetDiv.parentNode.insertBefore(badgeManagerDiv, targetDiv);
    badgeManagerDiv.appendChild(badgeOrderDiv)
    // targetDiv.parentNode.insertBefore(badgeOrderDiv, badgeManagerDiv);

    // 在这里添加您的自定义样式
    const customStyles = `
        .badge-manager {
            background-color: #f0f0f0; /* 背景颜色 */
            padding: 10px;             /* 内边距 */
            margin-bottom: 10px;       /* 底部外边距 */
            border: 1px solid #ccc;    /* 边框 */
            font-family: Arial, sans-serif; /* 字体 */
            color: #333;               /* 字体颜色 */
        }
        
        .badge-manager h2 {
            margin: 0;                /* 去掉默认的外边距 */
            font-size: 18px;          /* 标题字体大小 */
            color: #007BFF;           /* 标题颜色 */
        }

        .badge-manager p {
            margin: 5px 0;
        }

        .custom-button {
            padding: 5px 10px;
            margin-right: 5px;
            background-color: #007BFF;        /* 按钮背景颜色 */
            color: white;                      /* 字体颜色 */
            border: none;                      /* 去掉默认边框 */
            border-radius: 5px;               /* 圆角 */
            cursor: pointer;                   /* 鼠标悬停时显示手型 */
        }

        .custom-button:hover {
            background-color: #0056b3;        /* 悬停时的背景颜色 */
        }
    `;

    // 使用GM_addStyle函数将样式插入到页面中
    GM_addStyle(customStyles);

    // 添加功能按钮
    function createLink(label, onClickMethod) {
        const button = document.createElement('button');
        button.className = 'custom-button';
        button.textContent = label;
        button.onclick = (event) => {
            event.preventDefault(); // 阻止默认行为
            onClickMethod(); // 调用自定义方法
        };

        // 将链接添加到页面的 body 中
        const my_biaoti = document.querySelector('.badge-manager-button')
        my_biaoti.appendChild(button);
    }


    // 默认关闭回收功能
    // @params hide none是隐藏 block是显示
    createLink('显示/隐藏回收按钮', setHuiShou)
    setHuiShou('init')

    function setHuiShou(init) {
        document.querySelectorAll('button.pn').forEach(element => {
            if (element.innerText == '回收') {
                // 初始化干掉
                if (init) {
                    element.style.display = "none";
                    element.parentElement.style.display = "none";
                } else {
                    // 检查元素的display属性
                    if (element.style.display === "none" || getComputedStyle(element).display === "none") {
                        // 如果是none,则显示元素和其父元素
                        element.style.display = "inline";
                        element.parentElement.style.display = "inline"; // 显示父元素
                        // alert('回收按钮已显示')
                    } else {
                        // 否则隐藏元素和其父元素
                        element.style.display = "none";
                        element.parentElement.style.display = "none"; // 隐藏父元素
                        // alert('回收按钮已隐藏')
                    }
                }


            }
        })

    }

    // 勋章排序
    createLink('按照类型排序', kindOrder)

    function kindOrder() {
        // 获取所有匹配的元素
        const elements = document.querySelectorAll('.my_fenlei .myblok');
        const elementsArray = Array.from(elements);

        // 使用 map 函数处理每个元素
        const xunzhangList = elementsArray.map(myBlock => {
            const key = myBlock.getAttribute('key');
            const nameElement = myBlock.querySelector('p b'); // 找到包含名称的 <b> 标签
            const name = nameElement ? nameElement.textContent : '';
            return { [name]: key }
        })
        // 使用 reduce 合并字典
        const mergedDict = xunzhangList.reduce((acc, curr) => {
            return { ...acc, ...curr };
        }, {});

        // 填补未知的勋章
        const mergedDictKey = Object.keys(mergedDict)
        const allCategoriesData = Object.values(categoriesData).flat();
        categoriesData.other = findUniqueValues(mergedDictKey, allCategoriesData)

        function findUniqueValues(a, b) {
            // 将数组 b 转换为一个 Set,以提高查找效率
            const setB = new Set(b);

            // 过滤出在 a 中且不在 b 中的值
            const uniqueValues = a.filter(value => !setB.has(value));

            return uniqueValues;
        }

        const previousInput = localStorage.getItem('sortInput') || orderList.join(' ');

        // 弹出输入框,默认值为之前的内容
        const userInput = prompt("您正在进行一键排序,是否需要修改排序顺序(用空格分隔):", previousInput);

        // 如果用户输入了内容
        if (userInput !== null) {
            // 保存到 localStorage
            localStorage.setItem('sortInput', userInput);

            // 将输入的内容转换为数组并进行排序
            const sortedArray = userInput.split(' ').map(item => item.trim());
            const order1 = sortedArray.map(e => categoriesData[linkList[e]])
            const order2 = [].concat(...order1);
            const result = order2.map(key => mergedDict[key]).filter(value => value !== undefined);

            postOrder(result)


            // 输出排序后的结果
            // alert("排序后的结果:\n" + sortedArray.join(', '));
        }
    }

    //新增按钮保存/还原勋章顺序
    createLink('保存勋章顺序', saveKeysOrder)
    createLink('还原勋章顺序', loadKeysOrder)

    // 保存勋章顺序
    function saveArrayToLocalStorage(key, array) {
        localStorage.setItem(key, JSON.stringify(array));
    }

    // 从本地存储获取数组
    function getArrayFromLocalStorage(key) {
        const storedArray = localStorage.getItem(key);
        return storedArray ? JSON.parse(storedArray) : null;
    }

    // 从本地存储删除数组
    function removeArrayFromLocalStorage(key) {
        localStorage.removeItem(key);
    }

    // 获取所有具有指定类名的div元素
    function getKeysFromDivs(className) {
        // 使用querySelectorAll获取所有带有该类的div
        const divs = document.querySelectorAll(`div.${className}`);

        // 提取每个div的key属性并返回数组
        const keys = Array.from(divs).map(div => div.getAttribute('key'));

        return keys.join(',');
    }

    // 保存勋章顺序
    function saveKeysOrder() {
        const keys = getKeysFromDivs('myblok')
        saveArrayToLocalStorage('keyOrder', keys)
        alert('保存成功')
    }

    function loadKeysOrder() {
        const keys = getArrayFromLocalStorage('keyOrder')
        postOrder(keys)
    }

    function postOrder(newOrder) {
        const url = 'https://www.gamemale.com/plugin.php?id=wodexunzhang:showxunzhang'
        // 创建FormData对象
        const formData = new FormData();
        const data = { newOrder, action: 'newOrder' }

        // 将数据添加到formData
        for (const key in data) {
            if (data.hasOwnProperty(key)) {
                formData.append(key, data[key]);
            }
        }

        // 使用fetch发送POST请求
        fetch(url, {
            method: 'POST',
            body: formData,
        })
            .then(response => {
                // alert('还原勋章顺序成功,点击确认后刷新页面')
                location.reload()

                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }
                // return response.json(); // 或根据需要返回其他格式
            })
            .then(data => {
                console.log('Success:', data);
            })
            .catch(error => {
                console.error('Error:', error);
            });
    }

    // 勋章一键续期
    // 疑似存在扣了咒术不会增加时长的bug,暂停开发。似乎是编号的问题,再观察一下

    oneClickRenew()

    function oneClickRenew() {
        // 获取所有的按钮元素
        const buttons = document.querySelectorAll('button.pn');


        buttons.forEach(button => {
            // 检查onclick属性是否包含'可续期'
            if (button.innerText == '可续期') {
                // 创建新的一键续期按钮
                const newButton = document.createElement('button');
                const userMedalid = button.getAttribute('onclick').match(/\d+/g)[0]
                const titleElement = button.closest('.myimg').querySelector('p[title]');
                const name = titleElement.getAttribute('title');

                newButton.type = 'button';
                newButton.className = 'pn';
                newButton.innerHTML = '<em>一键续期</em>';
                newButton.onclick = function () {
                    // 弹出提示框询问续期多少次
                    const times = prompt(`您正在为【${name}】一键续期,请输入续期次数:`, "1");
                    const count = parseInt(times);

                    // 判断输入是否合法
                    if (isNaN(count) || count <= 0) {
                        alert("请输入有效的次数!");
                        return;
                    }

                    repeatRequest(count, 5000, userMedalid);
                };
                // 创建一个<p>标签来包裹新按钮
                const p = document.createElement('p');
                p.appendChild(newButton);

                // 将<p>标签插入到原按钮的父元素的父元素后面,并紧贴
                button.parentNode.insertAdjacentElement('afterend', p);
            }
        });

        function postRenew(userMedalid) {
            if (!userMedalid) return

            const url = 'https://www.gamemale.com/plugin.php?id=wodexunzhang:showxunzhang'
            // 创建FormData对象
            const formData = new FormData();
            const formhash = document.querySelector('input[name="formhash"]').value
            const data = { formhash, action: 'xuqi', jishoujiage: '', userMedalid }

            // 将数据添加到formData
            for (const key in data) {
                if (data.hasOwnProperty(key)) {
                    formData.append(key, data[key]);
                }
            }

            return fetch(url, { method: 'POST', body: formData, })
        }

        // 模拟网络请求的函数
        async function makeRequest(userMedalid) {
            try {
                // 假设这是一个真实的 API URL
                const response = await postRenew(userMedalid)

                // if (!response.ok) {
                //     throw new Error('网络请求失败');
                // }

                const data = await response.text();
                console.log("请求已发送:", data); // 打印响应数据
                return data; // 返回请求结果
            } catch (error) {
                console.error('请求出错:', error);
                throw error; // 抛出错误以供调用者处理
            }
        }

        // 显示提示信息的函数
        function showMessage(message) {
            const messageDiv = document.createElement('div');
            messageDiv.textContent = message;
            messageDiv.style.position = 'fixed';
            messageDiv.style.top = '10px';
            messageDiv.style.right = '10px';
            messageDiv.style.backgroundColor = '#4caf50'; // 绿色背景
            messageDiv.style.color = 'white';
            messageDiv.style.padding = '10px';
            messageDiv.style.zIndex = '1000';

            document.body.appendChild(messageDiv);

            // 自动消失
            setTimeout(() => {
                document.body.removeChild(messageDiv);
            }, 3000); // 3秒后消失
        }

        // 重复请求的函数
        async function repeatRequest(times, interval, userMedalid) {
            for (let i = 0; i < times; i++) {
                try {
                    const result = await makeRequest(userMedalid); // 等待请求完成
                    showMessage(`共需${times}次,已经请求 ${i + 1} 次`);
                } catch (error) {
                    showMessage(`请求 ${i + 1} 失败: ${error.message}`);
                }

                // 等待间隔
                if (i < times - 1) {
                    await new Promise(resolve => setTimeout(resolve, interval)); // 等待间隔
                }
            }
            showMessage('一键续期已完成,3秒后刷新页面');
            setTimeout(() => {
                location.reload()
            }, 3000); // 3秒后消失
            // console.log("所有请求已完成");
        }
    }

    // 新增从日志里获取勋章分类的函数    
    async function fetchBlogArticle() {
        const url = 'https://www.gamemale.com/home.php?mod=space&uid=723150&do=blog&quickforward=1&id=111238';

        try {
            const response = await fetch(url);

            if (!response.ok) {
                throw new Error('网络错误:' + response.status);
            }

            const html = await response.text();
            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');

            // 假设 blog_article 是一个 id 或者 class
            const articleElement = doc.querySelector('#blog_article'); // 根据实际情况调整选择器

            if (articleElement) {
                const articleText = articleElement.textContent || articleElement.innerText;

                // 尝试解析为 JSON
                try {
                    const articleArray = JSON.parse(articleText);

                    return articleArray;
                } catch (jsonError) {
                    console.error("无法解析为 JSON:", jsonError);
                    return []; // 返回空数组或处理错误的逻辑
                }
            } else {
                console.log('未找到 blog_article 元素');
                return []; // 返回空数组或处理未找到元素的逻辑
            }
        } catch (error) {
            console.error('发生错误:', error);
            return []; // 返回空数组或处理错误的逻辑
        }
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址