Steam寻找多余的卡牌

查找徽章满级但是仍然有卡牌的游戏

目前为 2021-12-13 提交的版本。查看 最新版本

// ==UserScript==
// @name            Find_Extra_card
// @name:zh-CN      Steam寻找多余的卡牌
// @namespace       https://blog.chrxw.com
// @version	        1.3
// @description	    查找徽章满级但是仍然有卡牌的游戏
// @description:zh-CN  查找徽章满级但是仍然有卡牌的游戏
// @author          Chr_
// @include         /https://steamcommunity\.com/(id|profiles)/[^\/]+/badges/?(\/$|\/\?)?/
// @supportURL      https://steamcn.com/t339531-1-1
// @license         AGPL-3.0
// @icon            https://blog.chrxw.com/favicon.ico
// @grant           GM_addStyle
// @grant           GM_setClipboard
// ==/UserScript==

(() => {
    'use strict';
    const WorkTread = 5; // 抓取线程
    const SleepTime = 50; // 抓取间隔

    const { origin, pathname } = window.location;
    const BadgeUrl = `${origin}${pathname}?sort=c&l=schinese&p=`;
    const RegPureBadges = RegExp(/<div class="badges_sheet">[\s\S]+<div class="profile_paging">/);
    const RegPureCards = RegExp(/<div class="badge_detail_tasks">[\s\S]+<div style="clear: left;">/);
    const Line = '==============================\n';

    let isWorking = false;

    init();
    //初始化
    function init() {
        const genBtn = (text, onclick) => {
            const btn = document.createElement('button');
            btn.textContent = text;
            btn.className = 'btn_medium fec_btn';
            btn.addEventListener('click', onclick);
            return btn;
        };
        const bar = document.querySelector('.profile_small_header_text');
        const btnHelp = genBtn('❔说明', () => {
            const { script: { version } } = GM_info;
            ShowAlertDialog(``, [
                `<h2>【插件版本 ${version}】</h2>`,
                `<p>【📇查找本页】:查找当前页面中,徽章已经满级(5级),但是库中仍然有多余卡牌的游戏</p>`,
                `<p><strike>【📇查找全部】:暂不可用</strike></p>`,
                '<p>【<a href="https://keylol.com/t772471-1-1" target="_blank">发布帖</a>】 【<a href=https://blog.chrxw.com/scripts.html target="_blank">脚本反馈</a>】 【Developed by <a href=https://steamcommunity.com/id/Chr_>Chr_</a>】</p>'
            ].join(''));
        });
        // const btnFindAll = genBtn('📇查找全部', findAllExtraCard);
        const btnFindOne = genBtn('📇查找本页', findCurrExtraCard);
        bar.appendChild(btnHelp);
        // bar.appendChild(btnFindAll);
        bar.appendChild(btnFindOne);
    }
    //读取当前页
    async function findCurrExtraCard() {
        const textArea = document.querySelector('textarea');
        textArea.className = 'fec_text';
        const [title, text, btnAbort] = showDialog();

        isWorking = true;
        btnAbort.disabled = false;
        title.innerText = '读取本页徽章信息';
        text.value += `开始运行 线程数量:${WorkTread}\n${Line}【持有】/【一套】 | 【游戏名】\n` + Line;

        const box = document.querySelector('.maincontent>.badges_sheet');
        if (box !== null) {
            const badges = parseDom2BadgeList(box);
            let count = 0;
            if (badges.length === 0) {
                text.value += '没有找到任何满级徽章\n';
            } else {
                title.innerText = `运行进度 【 0 / ${badges.length} 】`;
                for (let i = 0; i < badges.length && isWorking; i += WorkTread) {
                    const max = Math.min(i + WorkTread, badges.length);
                    const tasks = [];
                    for (let j = i; j < max; j++) {
                        const [url, title] = badges[j];
                        tasks.push(getCardInfo(url, title));
                    }
                    const values = await Promise.all(tasks);

                    for (const [succ, name, sum, total] of values) {
                        if (succ && sum > 0) {
                            count++;
                            text.value += `${sum} / ${total} | ${name}\n`;
                        }
                    }
                    title.innerText = `运行进度 【 ${i + WorkTread} / ${badges.length} 】`;
                    await aiosleep(SleepTime);
                }
            }
            text.value += Line + `共找到 ${count} 个徽章满级但仍有剩余卡牌的游戏\n`;
        } else {
            text.value += Line + '没有找到任何徽章\n';
        }
        isWorking = false;
        title.innerText = '运行结束';
        btnAbort.disabled = true;


    }
    //读取全部
    async function findAllExtraCard() {
        // const res = await getCardInfo('233', "https://steamcommunity.com/id/Chr_/gamecards/630060/");
        // console.log(res);
        const textArea = document.querySelector('textarea');
        textArea.className = 'fec_text';
        const [title, text, btnAbort] = showDialog();
    }
    //显示提示框
    function showDialog() {
        const genBtn = (text, onclick) => {
            const btn = document.createElement('button');
            btn.textContent = text;
            btn.className = 'btn_medium fec_btn';
            if (onclick) { btn.addEventListener('click', onclick); }
            return btn;
        };
        const area = document.createElement('div');
        area.className = 'fec_area';
        const tit = document.createElement('h2');
        tit.className = 'fec_title';
        tit.innerText = '';
        const txt = document.createElement('textarea');
        txt.className = 'fec_text';
        const action = document.createElement('div');
        action.className = 'fec_action';
        const btnAbort = genBtn('⛔停止运行', () => {
            if (isWorking) {
                isWorking = false;
                tit.innerText = '已停止';
            }
        });
        btnAbort.disabled = true;
        const btnClose = genBtn('❌关闭', null);
        const btnCopy = genBtn('📋复制', () => {
            GM_setClipboard(txt.value, 'text');
            btnCopy.innerText = '✅已复制';
            setTimeout(() => { btnCopy.innerText = '📋复制'; }, 1000);
        });
        action.appendChild(btnCopy);
        action.appendChild(btnAbort);
        action.appendChild(btnClose);
        area.appendChild(tit);
        area.appendChild(txt);
        area.appendChild(action);
        const diag = ShowDialog('', area, { bExplicitDismissalOnly: true });
        btnClose.addEventListener('click', () => { diag.Dismiss(); });
        return [tit, txt, btnAbort];
    }
    //异步Sleep
    function aiosleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    //解析徽章列表的DOM节点
    function parseDom2BadgeList(ele) {
        const badges = ele.querySelectorAll('.badge_row.is_link');
        let maxBadges = [];
        for (const badge of badges) {
            const url = badge.querySelector('a.badge_row_overlay')?.href;
            const level = badge.querySelector('.badge_info_description>div:nth-child(2)')?.innerText.trim() ?? "0 级";
            const title = badge.querySelector('.badge_title')?.innerText.trim() ?? "Null";
            if (url && level && level.startsWith('5 级')) {
                maxBadges.push([url, title]);
            }
        }
        return maxBadges;
    }
    //读取卡牌页面
    function getCardInfo(url, title) {
        return new Promise((resolve, reject) => {
            fetch(url)
                .then(res => res.text())
                .then(html => {
                    const pureHtml = RegPureCards.exec(html)[0];
                    let box = document.createElement('div');
                    box.style.display = 'none';
                    box.innerHTML = pureHtml;

                    const cardCount = box.querySelectorAll('.badge_card_set_text_qty');
                    const cardTotal = cardCount.length;

                    if (cardTotal === 0) { resolve([true, title, 0, 0]); }

                    let sum = 0;
                    for (let i = 0; i < cardTotal; i++) {
                        let text = cardCount[i].innerText;
                        let num = text.substring(1, text.length - 1);
                        try {
                            sum += parseInt(num);
                        } catch (e) {
                            console.error(e);
                        }
                    }
                    document.body.appendChild(box);
                    document.body.removeChild(box);

                    resolve([true, title, sum, cardTotal]);
                })
                .catch(err => {
                    console.error('请求失败', err);
                    resolve([false, null, null, null]);
                });
        });
    }
    //读取徽章页面
    async function getBadgeList(page) {
        await fetch(BadgeUrl + page)
            .then(res => res.text())
            .then(html => {
                const pureHtml = RegPureBadges.exec(html)[0];
                let box = document.createElement('div');
                box.style.display = 'none';
                box.innerHTML = pureHtml;
                let badges = parseDom2BadgeList(box);
                badges.forEach(badge => {
                    const url = badge.querySelector('a')?.href;
                    const badgeInfo = badge.querySelector('.badge_info_description>div:nth-child(2)')?.innerText.trim();
                    if (url === null || badgeInfo === null) { return; }

                    if (badgeInfo.startsWith('5 级')) {
                        console.log(`${badgeInfo}`);
                    }
                });
                document.body.appendChild(box);
                document.body.removeChild(box);
            });
    }


})();

GM_addStyle(`
.profile_small_header_text > .fec_btn {
    float: right;
  }
  .profile_small_header_text > .fec_btn {
    margin-left: 5px;
  }
  .fec_action > .fec_btn:not(:first-child) {
    margin-left: 20px;
  }
  .fec_btn {
    padding: 3px 6px;
  }
  .fec_action {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    margin-top: 5px;
  }
  .fec_action > .fec_btn {
    flex: 0 0 auto;
  }
  .fec_text {
    height: 300px;
    width: 600px;
    resize: vertical;
    font-size: 15px;
    margin: 5px 0;
    padding: 5px;
    background-color: rgba(0, 0, 0, 0.4);
    color: #fff;
    border: 1 px solid #000;
    border-radius: 3 px;
    box-shadow: 1px 1px 0px #45556c;
  }
  .fec_area {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    width: 100%;
    height: 100%;
  }
  .fec_area > * {
    width: 100%;
  }
  
`);

QingJ © 2025

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