朱雀自动转盘抽奖及统计

实现朱雀的抽奖功能,自动获取CSRF Token与Cookie,并显示抽奖结果,支持进度显示和停止功能,抽奖之后可以一键出售道具和上传,可以设置一键出售预设

// ==UserScript==
// @name         朱雀自动转盘抽奖及统计
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  实现朱雀的抽奖功能,自动获取CSRF Token与Cookie,并显示抽奖结果,支持进度显示和停止功能,抽奖之后可以一键出售道具和上传,可以设置一键出售预设
// @author       banner、KoWming
// @match        https://zhuque.in/*
// @grant        none
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // 定义奖品信息和对应价值
    const prizeMap = {
        "1": { "Name": "一等奖: 改名卡", "Value": 240000 },
        "2": { "Name": "二等奖: 神佑(VIP) 7 天卡", "Value": 80000 },
        "3": { "Name": "三等奖: 邀请卡", "Value": 64000 },
        "4": { "Name": "四等奖: 自动释放技能道具卡 7 天卡", "Value": 24000 },
        "5": { "Name": "五等奖: 上传 20 GiB", "Value": 2280 },
        "6": { "Name": "六等奖: 上传 10 GiB", "Value": 1140 },
        "7": { "Name": "未中奖", "Value": 0 }
    };

    // 全局状态变量
    let isRunning = false;
    let stopRequested = false;
    let totalCost = 0;
    let totalValue = 0;
    let prizeSummary = {};
    let totalUpload = 0;  // 记录上传的总量

    // 初始化奖品统计
    function resetStats() {
        totalCost = 0;
        totalValue = 0;
        totalUpload = 0;
        prizeSummary = {};
        Object.keys(prizeMap).forEach(prizeId => {
            prizeSummary[prizeId] = { "Count": 0, "TotalValue": 0 };
        });
    }

    // 创建UI界面
    function createUI() {
        setTimeout(() => {
            const container = document.createElement('div');
            container.style.position = 'fixed';
            container.style.top = '10px';
            container.style.right = '10px';
            container.style.backgroundColor = '#fff';
            container.style.padding = '20px';
            container.style.border = '1px solid #ccc';
            container.style.boxShadow = '0px 4px 8px rgba(0,0,0,0.1)';
            container.style.zIndex = '9999';

            const title = document.createElement('h3');
            title.textContent = '抽奖统计';
            container.appendChild(title);

            // 添加设置按钮
            const settingsButton = document.createElement('button');
            settingsButton.textContent = '一键出售预设';
            settingsButton.style.margin = '5px 0 10px 0';
            container.appendChild(settingsButton);

            // 设置UI容器(初始隐藏)
            const settingsContainer = document.createElement('div');
            settingsContainer.style.display = 'none';
            settingsContainer.style.marginTop = '10px';
            settingsContainer.style.borderTop = '1px solid #ccc';
            settingsContainer.style.paddingTop = '10px';
            container.appendChild(settingsContainer);

            // 设置项定义
            const sellSettings = [
                { id: 1, label: '自动回收 改名卡', key: 'sell_item_1' },
                { id: 2, label: '自动回收 神佑(VIP) 7天卡', key: 'sell_item_2' },
                { id: 3, label: '自动回收 邀请卡', key: 'sell_item_3' },
                { id: 4, label: '自动回收 自动释放技能道具卡7天卡', key: 'sell_item_4' },
                { id: 5, label: '自动出售上传量(20GiB/10GiB)', key: 'sell_upload' }
            ];

            // 渲染设置UI
            function renderSettingsUI() {
                settingsContainer.innerHTML = '<h4>一键出售预设设置</h4>';
                const currentUploadPrice = getSellUploadPrice();
                sellSettings.forEach(setting => {
                    const div = document.createElement('div');
                    const checkbox = document.createElement('input');
                    checkbox.type = 'checkbox';
                    checkbox.id = setting.key;
                    checkbox.checked = getSellSetting(setting.key);
                    let valueText = '';
                    if (setting.id >= 1 && setting.id <= 4) {
                        valueText = `(价值 ${prizeMap[String(setting.id)].Value} 灵石)`;
                    } else if (setting.id === 5) {
                        valueText = `(单价 ${currentUploadPrice} 灵石/GiB)`;
                    }
                    const label = document.createElement('label');
                    label.textContent = setting.label + valueText;
                    label.htmlFor = setting.key;
                    div.appendChild(checkbox);
                    div.appendChild(label);
                    settingsContainer.appendChild(div);
                });
                // 上传量单价输入框
                const priceDiv = document.createElement('div');
                priceDiv.style.marginTop = '10px';
                const priceLabel = document.createElement('label');
                priceLabel.textContent = '上传量卖出单价(灵石/GiB):';
                priceLabel.htmlFor = 'sell_upload_price';
                priceDiv.appendChild(priceLabel);
                const priceInput = document.createElement('input');
                priceInput.type = 'number';
                priceInput.id = 'sell_upload_price';
                priceInput.min = '1';
                priceInput.style.width = '80px';
                priceInput.value = currentUploadPrice;
                priceDiv.appendChild(priceInput);
                settingsContainer.appendChild(priceDiv);
                // 按钮区域
                const btnDiv = document.createElement('div');
                btnDiv.style.marginTop = '10px';
                // 保存按钮
                const saveBtn = document.createElement('button');
                saveBtn.textContent = '保存并返回';
                saveBtn.addEventListener('click', () => {
                    sellSettings.forEach(setting => {
                        const checked = settingsContainer.querySelector(`#${setting.key}`).checked;
                        localStorage.setItem(setting.key, checked ? '1' : '0');
                    });
                    // 保存上传量单价
                    let price = parseInt(settingsContainer.querySelector('#sell_upload_price').value, 10);
                    if (isNaN(price) || price < 1) price = 127;
                    localStorage.setItem('sell_upload_price', price);
                    settingsContainer.style.display = 'none';
                    inputContainer.style.display = '';
                    button.style.display = '';
                    resultContainer.style.display = '';
                    settingsButton.style.display = '';
                });
                btnDiv.appendChild(saveBtn);
                // 恢复默认按钮
                const resetBtn = document.createElement('button');
                resetBtn.textContent = '恢复默认';
                resetBtn.style.marginLeft = '10px';
                resetBtn.addEventListener('click', () => {
                    sellSettings.forEach(setting => {
                        localStorage.removeItem(setting.key);
                    });
                    localStorage.removeItem('sell_upload_price');
                    renderSettingsUI(); // 重新渲染设置UI
                });
                btnDiv.appendChild(resetBtn);
                settingsContainer.appendChild(btnDiv);
            }

            // 获取设置
            function getSellSetting(key) {
                const val = localStorage.getItem(key);
                if (val === null) {
                    // 默认:除邀请卡外都勾选
                    if (key === 'sell_item_3') return false;
                    return true;
                }
                return val === '1';
            }
            function getSellUploadPrice() {
                const val = localStorage.getItem('sell_upload_price');
                return val && !isNaN(val) && parseInt(val, 10) > 0 ? parseInt(val, 10) : 127;
            }

            // 设置按钮点击事件:切换到设置UI
            settingsButton.addEventListener('click', () => {
                renderSettingsUI();
                settingsContainer.style.display = '';
                inputContainer.style.display = 'none';
                button.style.display = 'none';
                resultContainer.style.display = 'none';
                settingsButton.style.display = 'none';
            });

            // 输入区域
            const inputContainer = document.createElement('div');
            inputContainer.style.margin = '10px 0';

            // 抽奖次数输入
            const timesDiv = document.createElement('div');
            timesDiv.style.marginBottom = '10px';
            timesDiv.innerHTML = '<label>抽奖次数: </label><input type="number" value="60" min="1" style="width: 80px;">';
            inputContainer.appendChild(timesDiv);

            // 间隔时间输入
            const intervalDiv = document.createElement('div');
            intervalDiv.style.marginBottom = '10px';
            intervalDiv.innerHTML = '<label>间隔(ms): </label><input type="number" value="300" min="0" style="width: 80px;">';
            inputContainer.appendChild(intervalDiv);

            container.appendChild(inputContainer);

            // 控制按钮
            const button = document.createElement('button');
            button.textContent = '开始抽奖';
            button.style.margin = '10px 0';
            container.appendChild(button);

            // 结果显示区域
            const resultContainer = document.createElement('div');
            resultContainer.style.marginTop = '20px';
            resultContainer.style.borderTop = '1px solid #ccc';
            resultContainer.style.paddingTop = '10px';
            container.appendChild(resultContainer);

            document.body.appendChild(container);

            // 事件监听
            button.addEventListener('click', async () => {
                if (isRunning) {
                    stopRequested = true;
                    return;
                }

                const timesInput = timesDiv.querySelector('input');
                const intervalInput = intervalDiv.querySelector('input');

                const requestTimes = parseInt(timesInput.value) || 10;
                const interval = parseInt(intervalInput.value) || 1000;

                if (requestTimes < 1) {
                    alert('抽奖次数至少为1次');
                    return;
                }

                if (interval < 300) {
                    alert('为了避免给站点服务器带来较大负荷,不建议300ms一下的间隔,建议将间隔时长调制500ms以上');
                    return;
                }

                isRunning = true;
                stopRequested = false;
                button.textContent = '停止抽奖';
                resetStats();

                resultContainer.innerHTML = `
                    <div class="status">准备中...</div>
                    <div class="progress">当前进度: 0/${requestTimes}</div>
                    <div class="results"></div>
                    <button class="sellButton" style="display: none;">一键出售</button>
                `;

                try {
                    for (let i = 1; i <= requestTimes; i++) {
                        if (stopRequested) break;

                        await sendRequest();
                        await new Promise(r => setTimeout(r, interval));

                        // 更新进度显示
                        resultContainer.querySelector('.progress').textContent =
                            `当前进度: ${i}/${requestTimes}`;
                    }
                } finally {
                    isRunning = false;
                    button.textContent = '开始抽奖';
                    updateResults(resultContainer);

                    // 显示出售按钮
                    const sellButton = resultContainer.querySelector('.sellButton');
                    sellButton.style.display = 'inline-block';

                    // 销售按钮点击事件
                    sellButton.addEventListener('click', async () => {
                        await handleSellRequest(); // 执行出售请求
                    });
                }
            });
        }, 1000);  // 延迟1秒执行UI创建
    }

    // 发送抽奖请求
    async function sendRequest() {
        try {
            const response = await fetch("https://zhuque.in/api/gaming/spinThePrizeWheel", {
                method: "POST",
                headers: {
                    "X-Csrf-Token": document.querySelector('meta[name="x-csrf-token"]').content,
                    "Cookie": document.cookie
                }
            });

            if (response.ok) {
                const data = await response.json();
                const prizeId = String(data.data.prize);

                if (prizeMap[prizeId]) {
                    prizeSummary[prizeId].Count++;
                    prizeSummary[prizeId].TotalValue += prizeMap[prizeId].Value;
                    totalCost += 1500;
                    totalValue += prizeMap[prizeId].Value;

                    // 处理上传量
                    if (prizeId === "5") {
                        totalUpload += 20;  // 上传20 GiB
                    } else if (prizeId === "6") {
                        totalUpload += 10;  // 上传10 GiB
                    }
                }
            }
        } catch (error) {
            console.error('请求失败:', error);
        }
    }

    // 更新结果展示
    function updateResults(container) {
        let html = '<h4>抽奖结果:</h4>';

        // 按价值降序排列
        const sorted = Object.entries(prizeMap)
            .sort((a, b) => b[1].Value - a[1].Value)
            .forEach(([id, prize]) => {
                const count = prizeSummary[id].Count;
                html += `${prize.Name}: ${count}次 (价值 ${prizeSummary[id].TotalValue}灵石)<br>`;
            });

        html += `<br>总消耗: ${totalCost}灵石<br>`;
        html += `总价值: ${totalValue}灵石<br>`;
        html += `<b>净${totalValue >= totalCost ? '盈利' : '亏损'}: ${Math.abs(totalValue - totalCost)}灵石</b>`;

        container.querySelector('.results').innerHTML = html;
    }

    // 处理出售请求(包括回收与出售上传量)
    async function handleSellRequest() {
        let allRecycleSuccess = true;  // 标记是否所有回收操作都成功

        // 首先回收道具:ID 1, 2, 3, 4
        for (let id = 1; id <= 4; id++) {
            const prizeCount = prizeSummary[id] ? prizeSummary[id].Count : 0;
            // 判断设置
            if (prizeCount > 0 && (localStorage.getItem(`sell_item_${id}`) === '1')) {
                // 回收每个道具的数量
                const recycleSuccess = await recycleMagicCard(id, prizeCount);
                if (!recycleSuccess) {
                    allRecycleSuccess = false;  // 如果有回收失败,标记为失败
                }
            }
        }

        // 然后出售上传量:仅处理 ID 为 5 和 6 的上传量
        if (totalUpload > 0 && (localStorage.getItem('sell_upload') === '1')) {
            // 计算出售的总上传量,按 GiB 出售
            const uploadPrice = localStorage.getItem('sell_upload_price');
            const price = uploadPrice && !isNaN(uploadPrice) && parseInt(uploadPrice, 10) > 0 ? parseInt(uploadPrice, 10) : 127;
            const totalBonus = price * totalUpload; // 每 GiB price 灵石
            const response = await fetch("https://zhuque.in/api/transaction/create", {
                method: "POST",
                headers: {
                    "X-Csrf-Token": document.querySelector('meta[name="x-csrf-token"]').content,
                    "Cookie": document.cookie,
                    "Content-Type": "application/json"
                },
                body: JSON.stringify({
                    type: 2,
                    unit: "GiB",
                    bonus: totalBonus,
                    upload: totalUpload
                })
            });

            if (response.ok) {
                console.log(`成功提交,交易上传 ${totalUpload} GiB,预计获得 ${totalUpload * price} 灵石`);
            } else {
                const errorText = await response.text();  // 获取错误信息
                console.log(`出售失败: ${errorText}`);
                allRecycleSuccess = false;  // 如果有回收失败,标记为失败
            }
        } else {
            if (totalUpload > 0) console.log('未勾选自动出售上传量,未出售');
            else console.log('没有上传量可出售');
        }

        // 如果所有道具回收成功,提示用户
        if (allRecycleSuccess) {
            console.log('所有道具回收成功!');
            alert('所有道具回收成功!');
        } else {
            alert('道具回收失败,错误详情查看控制台')
        }
    }

    // 回收道具:ID 1, 2, 3, 4
    async function recycleMagicCard(id, count) {
        let success = true;  // 标记是否回收成功

        // 回收多个道具的情况,循环回收 `count` 次
        for (let i = 0; i < count; i++) {
            const response = await fetch("https://zhuque.in/api/mall/recycleMagicCard", {
                method: "POST",
                headers: {
                    "X-Csrf-Token": document.querySelector('meta[name="x-csrf-token"]').content,
                    "Cookie": document.cookie,
                    "Content-Type": "application/json"
                },
                body: JSON.stringify({ id: id })
            });

            if (response.ok) {
                console.log(`道具 ID ${id} 第 ${i + 1} 次回收成功!`);
            } else {
                const errorText = await response.text();
                console.log(`回收失败: ${errorText}`);
                success = false;  // 如果有任何失败,标记为失败
            }

            // 添加 100ms 的延迟
            await new Promise(r => setTimeout(r, 100));
        }

        return success;  // 返回回收是否成功
    }

    // 初始化
    resetStats();
    createUI();
})();

QingJ © 2025

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