Steam🎮-100%

Steam -100% with semi-auto-download for IGG

当前为 2025-11-08 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name              Steam🎮-100%
// @name:ar           ستيم 🎮-100%
// @name:bg           Steam🎮-100%
// @name:cs           Steam🎮-100%
// @name:da           Steam🎮-100%
// @name:de           Steam🎮-100%
// @name:el           Steam🎮-100%
// @name:en           Steam🎮-100%
// @name:es           Steam🎮-100%
// @name:fi           Steam🎮-100%
// @name:fr           Steam🎮-100%
// @name:fr-CA        Steam🎮-100%
// @name:he           סטים 🎮-100%
// @name:hu           Steam🎮-100%
// @name:id           Steam🎮-100%
// @name:it           Steam🎮-100%
// @name:ja           スチーム🎮-100%
// @name:ko           스팀🎮-100%
// @name:nl           Steam🎮-100%
// @name:no           Steam🎮-100%
// @name:pl           Steam🎮-100%
// @name:pt           Steam🎮-100%
// @name:ro           Steam🎮-100%
// @name:ru           Steam🎮-100%
// @name:sk           Steam🎮-100%
// @name:sv           Steam🎮-100%
// @name:th           สตีม🎮-100%
// @name:tr           Steam🎮-100%
// @name:uk           Steam🎮-100%
// @name:vi           Steam🎮-100%
// @name:zh           Steam🎮-100%
// @description       Steam -100% with semi-auto-download for IGG
// @description:ar    ستيم -100٪ مع تحميل شبه تلقائي لـ IGG
// @description:bg    Steam -100% със полуавтоматично изтегляне за IGG
// @description:cs    Steam -100% s poloautomatickým stahováním pro IGG
// @description:da    Steam -100% med semi-automatisk download til IGG
// @description:de    Steam -100% mit halbautomatischem Download für IGG
// @description:el    Steam -100% με ημι-αυτόματη λήψη για IGG
// @description:en    Steam -100% with semi-auto-download for IGG
// @description:es    Steam -100% con descarga semi-automática para IGG
// @description:fi    Steam -100% puoliautomaattisella latauksella IGG:lle
// @description:fr    Steam -100% avec semi-téléchargement automatique pour IGG
// @description:fr-CA    Steam -100% avec semi-téléchargement automatique pour IGG
// @description:he    Steam -100% עם הורדה חצי-אוטומטית עבור IGG
// @description:hu    Steam -100% félautomatikus letöltéssel IGG-hez
// @description:id    Steam -100% dengan unduhan semi-otomatis untuk IGG
// @description:it    Steam -100% con download semi-automatico per IGG
// @description:ja    Steam -100%:IGG用の半自動ダウンロード対応
// @description:ko    Steam -100% IGG용 반자동 다운로드 지원
// @description:nl    Steam -100% met semi-automatische download voor IGG
// @description:no    Steam -100% med semi-automatisk nedlasting for IGG
// @description:pl    Steam -100% z półautomatycznym pobieraniem dla IGG
// @description:pt    Steam -100% com semi-download automático para IGG
// @description:ro    Steam -100% cu descărcare semi-automată pentru IGG
// @description:ru    Steam -100% с полуавтоматической загрузкой для IGG
// @description:sk    Steam -100% s poloautomatickým sťahovaním pre IGG
// @description:sv    Steam -100% med semi-automatisk nedladdning för IGG
// @description:th    Steam -100% พร้อมการดาวน์โหลดกึ่งอัตโนมัติสำหรับ IGG
// @description:tr    Steam -100% IGG için yarı otomatik indirme ile
// @description:uk    Steam -100% з напівавтоматичним завантаженням для IGG
// @description:vi    Steam -100% với tải xuống bán tự động cho IGG
// @description:zh    Steam -100%,支持 IGG 半自动下载
// @version      1.1.6
// @match        *store.steampowered.com/app/*
// @include      *igg-games.com/*-free-download.html?cdl*
// @match        https://online-fix.me/games/*
// @run-at       document-end
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addValueChangeListener
// @grant        GM_xmlhttpRequest
// @connect      skidrowreloaded.com
// @connect      fitgirl-repacks.site
// @connect      online-fix.me
// @license      MIT
// @namespace    DEV314R
// ==/UserScript==

(function() {
"use strict";

// ---------- Utils DOM ----------
const $ = s => document.querySelector(s);
const $$ = s => [...document.querySelectorAll(s)];

// ---------- Labels ----------
const LABELS = {
    dl: { en:"Download",ar:"تحميل",bg:"Изтегли",
  "zh-CN":"下载","zh-TW":"下載",
  cs:"Stáhnout",da:"Download",nl:"Downloaden",
  fi:"Lataa", fr:"Télécharger",de:"Herunterladen",
  el:"Λήψη",hu:"Letöltés", id:"Unduh",
  it:"Scarica",ja:"ダウンロード",ko:"다운로드",
  no:"Last ned",pl:"Pobierz",
  "pt-PT":"Transferir","pt-BR":"Baixar",
  ro:"Descărcare", ru:"Скачать",
  "es-ES":"Descargar","es-LA":"Bajar",
  sv:"Ladda ner", th:"ดาวน์โหลด", tr:"İndir",
  uk:"Завантажити", vi:"Tải xuống"},

    multi: {en:"Multiplayer",ar:"متعدد اللاعبين",bg:"Мултиплейър",
  "zh-CN":"多人游戏","zh-TW":"多人遊戲",
  cs:"Multiplayer",da:"Flerspiller", nl:"Multiplayer",
  fi:"Moninpeli",fr:"Multijoueur",de:"Mehrspieler",
  el:"Πολλαπλών παικτών", hu:"Többjátékos",id:"Multipemain",
  it:"Multigiocatore",ja:"マルチプレイヤー",ko:"멀티플레이어",
  no:"Flerspiller",pl:"Wieloosобowy",
  "pt-PT":"Multijogador","pt-BR":"Multijogador",
  ro:"Multiplayer",ru:"Многопользовательская игра",
  "es-ES":"Multijugador","es-LA":"Multijugador",
  sv:"Flerspelarläge",th:"ผู้เล่นหลายคน", tr:"Çok Oyunculu",
  uk:"Багатокористувацька",vi:"Nhiều người chơi"},

    source: { en:"Source",ar:"المصدر",bg:"Източник",
  "zh-CN":"来源","zh-TW":"來源",
  cs:"Zdroj",da:"Kilde",nl:"Bron",
  fi:"Lähde",fr:"Source",de:"Quelle",
  el:"Πηγή",hu:"Forrás",id:"Sumber",
  it:"Fonte",ja:"ソース",ko:"출처",
  no:"Kilde",pl:"Źródło",
  "pt-PT":"Fonte","pt-BR":"Fonte",
  ro:"Sursă",ru:"Источник",
  "es-ES":"Fuente","es-LA":"Fuente",
  sv:"Källa",th:"แหล่งที่มา",tr:"Kaynak",
  uk:"Джерело",vi:"Nguồn"},

    unavailable: { en:"unavailable",ar:"غير متوفر",bg:"Недостъпно",
  "zh-CN":"不可用","zh-TW":"不可用",
  cs:"Nedostupné",da:"Ikke tilgængelig",nl:"Niet beschikbaar",
  fi:"Ei saatavilla",fr:"indisponible",de:"Nicht verfügbar",
  el:"Μη διαθέσιμο",hu:"Nem elérhető",id:"Tidak tersedia",
  it:"Non disponibile",ja:"利用不可",ko:"사용 불가",
  no:"Utilgjengelig",pl:"Niedostępne",
  "pt-PT":"Indisponível","pt-BR":"Indisponível",
  ro:"Indisponibil",ru:"Недоступно",
  "es-ES":"No disponible","es-LA":"No disponible",
  sv:"Inte tillgängligt",th:"ไม่พร้อมใช้งาน",tr:"Mevcut değil",
  uk:"Недоступно",vi:"Không khả dụng" }

};
const lang = navigator.language.toLowerCase();
const langKey = (() => {
    if(lang.startsWith("pt-br")) return "pt-BR";
    if(lang.startsWith("pt-pt")) return "pt-PT";
    if(lang.startsWith("zh-tw")||lang.startsWith("zh-hk")) return "zh-TW";
    if(lang.startsWith("zh")) return "zh-CN";
    if(lang.startsWith("es-")) return ["es-mx","es-ar","es-co","es-cl","es-pe","es-ve","es-uy","es-bo","es-py","es-cr","es-pa","es-do","es-ec","es-gt","es-hn","es-ni","es-sv","es-pr","es-419"].includes(lang) ? "es-LA" : "es-ES";
    return lang.split("-")[0];
})();
const dlLabel = LABELS.dl[langKey]||LABELS.dl.en;
const multiLabel = LABELS.multi[langKey]||LABELS.multi.en;
const sourceLabel = LABELS.source[langKey]||LABELS.source.en;
const unavailableLabel = LABELS.unavailable[langKey]||LABELS.unavailable.en;

// ---------- Base utils ----------
const norm = s => s.normalize("NFD").replace(/[\u0300-\u036f]|[©®™℠]|\?/g,"").replace(/[:'–—]/g,"").trim().toLowerCase();
const slugify = s => norm(s)//.replace(/\s+/g,"-");

// ---------- Platform ----------
const defaultData = { IGG:{source:"1Fichier"}, Skidrow:{source:"MEGA"}, FitGirl:{source:"DataNodes"} };
let platformData = GM_getValue("platformData", defaultData);
let platform = GM_getValue("platform", "Skidrow");
platformData[platform] ||= { source: defaultData[platform].source };
GM_setValue("platformData", platformData);
let sdl = platformData[platform].source;

function setPlatform(p){
    platform = p;
    GM_setValue("platform", p);
    platformData[p] ||= { source: defaultData[p].source };
    sdl = platformData[p].source;
    GM_setValue("platformData", platformData);
    renderSourceFlyout();
    updateDownloadLink(true);
}
function setSourceForPlatform(src){
    platformData[platform].source = src;
    GM_setValue("platformData", platformData);
    sdl = src;
}

// ---------- Styles ----------
const style = document.createElement("style");
style.textContent = `
.menu{font-family:Arial,sans-serif;font-size:1em;position:relative;user-select:none;padding:.1em;display:inline-flex}
.menu-btn{background:#67c1f533;color:#67c1f5;padding:.4em .6em;border-radius:.3em;cursor:pointer}
.menu-btn:hover{background:#67c1f5bb;color:#fff}
.flyout{border:none;border-radius:.3em;background:#23262E;position:absolute;top:100%;left:0;display:none;z-index:10000}
.menu:hover .flyout{display:block}
.flyout a{display:block;padding:.4em .6em;background:#23262E;color:#ddd;text-decoration:none}
.flyout a:hover{background:#85949d59;color:#fff}
.menu-container{right:.75em;top:.75em;z-index:9999;position:fixed}`;
document.head.appendChild(style);

// ---------- GM fetch ----------
const gmFetchHTML = (url,timeout=10000) => new Promise(resolve=>{
    if(typeof GM_xmlhttpRequest !== "function") return resolve(null);
    let done = false;
    const timer = setTimeout(()=>{if(!done){done=true; resolve(null);}}, timeout);
    GM_xmlhttpRequest({
        method: "GET",
        url,
        onload: r => { if(!done){ done=true; clearTimeout(timer); resolve(new DOMParser().parseFromString(r.responseText,"text/html")); } },
        onerror: ()=>{ if(!done){ done=true; clearTimeout(timer); resolve(null); } }
    });
});

// ---------- Game Name Cache ----------
let gameNameCache = null;
let gameNameFetched = false;
const ensureGameName = () => gameNameCache;
const fetchGameNameIfNeeded = async () => {
    if(gameNameFetched) return gameNameCache;
    gameNameFetched = true;
    const local = $('span[itemprop="name"]')?.textContent || $("#appHubAppName")?.innerText || "";
    if(local) { gameNameCache = slugify(local); return gameNameCache; }
    const doc = await gmFetchHTML(location.origin + location.pathname + "?l=english");
    const name = doc?.querySelector('span[itemprop="name"]')?.textContent || "";
    gameNameCache = name ? slugify(name) : null;
    return gameNameCache;
};

// ---------- Spinner ----------
function startLoadingAnim(span){
    if(!span) return;
    const f=["⠁","⠃","⠇","⠧","⠷","⠿","⠻","⠟","⠯","⠷","⠧","⠇","⠃"];
    stopLoadingAnim(span,"");
    let i=0;
    span._timer = setInterval(()=>{span.textContent=f[i=++i%f.length]+" "+dlLabel;},90);
}
function stopLoadingAnim(span,txt){
    if(!span) return;
    clearInterval(span._timer);
    delete span._timer;
    span.textContent = txt;
}

// ---------- Step By Step ----------
const skidrowLinkCache = new Map();

async function SkidrowStep(slug, enc){
    if(!slug) return "🐞❎";
    const cacheKey = slug + "::" + enc.toLowerCase();
    if(skidrowLinkCache.has(cacheKey)) return skidrowLinkCache.get(cacheKey);
    const q = slug;
    const searchDoc = await gmFetchHTML(`https://www.skidrowreloaded.com/?s=${encodeURIComponent(q)}`);
    if(!searchDoc) return "🐞❎";

    const slugNorm = slug.replace(/-/g," ").normalize("NFD").replace(/(\s|-)?[^A-Za-z0-9\s]/gi, "").toLowerCase();
    const encNorm = enc.toLowerCase();
    const post = [...searchDoc.querySelectorAll("[class^=post] h2 a[href]")]
        .find(a => a.textContent.toLowerCase().includes(slugNorm));
    if(!post) return "❌ "+unavailableLabel;
    const postDoc = await gmFetchHTML(post.href);
    if(!postDoc) return "🐞❎";
    const link = [...postDoc.querySelectorAll("[id^=tabs] p strong")]
        .find(s => s.textContent.toLowerCase().includes(encNorm))
        ?.closest("p")?.querySelector("a[href]")?.href || null;
    const result = link || sourceLabel+" "+unavailableLabel;
    skidrowLinkCache.set(cacheKey, result);
    return result;
}

async function FitGirlStep(slug,enc){
    if(!slug) return "🐞❎";
    const searchDoc = await gmFetchHTML(`https://fitgirl-repacks.site/${slug}`);
    if(!searchDoc) return "🐞❎";
    const encNorm = enc.replace(/Filehoster: /g,"").toLowerCase();
    const link = [...searchDoc.querySelectorAll(".entry-content > ul > li > a[href]")]
                 .find(a=>a.textContent.toLowerCase().includes(encNorm))?.href || null;
    return link || "❌ "+unavailableLabel;
}

// ---------- Apply Link ----------
let LinkCache = null;
function applyLink(dl){
    if(!dl) return;
    const enc = sdl;
    if(platform==="IGG"){
        dl.href=`https://igg-games.com/${gameNameCache.replace(/(\s|-)?[^A-Za-z0-9\s]/gi, "").replace(/\s/gi,"-")}-free-download.html?cdl&src=${enc}`;
        dl.target="_blank"; dl.onclick=null;
    } else {
        dl.href="#";
        dl.onclick = async e=>{
            e.preventDefault();
            const span = dl.querySelector("span");
            if(LinkCache && !LinkCache.startsWith("❎") && !LinkCache.startsWith("❌")){
                window.open(LinkCache,"_blank"); return;
            }
            startLoadingAnim(span);
            LinkCache = platform==="FitGirl" ? await FitGirlStep(gameNameCache.replace(/(\s|-)?[^A-Za-z0-9\s]/gi, "").replace(/\s/gi,"-"),enc) : await SkidrowStep(gameNameCache,enc);
            stopLoadingAnim(span, LinkCache.startsWith("http") ? dlLabel : LinkCache);
            if(LinkCache.startsWith("http")) window.open(LinkCache,"_blank");
        };
    }
}

// ---------- Reset labels ----------
document.addEventListener("click", e=>{
    if(!e.target.closest(".flyout")) return;
    LinkCache=null;
    $$("span#add314, .download-btn span").forEach(s=>s.textContent=dlLabel);
});

// ---------- Flyouts ----------
const sourcesByPlatform = {
    IGG:["1Fichier","MegaUp.net","Mega.nz","GoFile","MixDrop","Rapidgator","Bowfile","SendCM","Google Drive"],
    Skidrow:["MEGA","1FICHIER","PIXELDRAIN","MEDIAFIRE","GOFILE","VIKINGFILE","BOWFILE","1CLOUDFILE","MEGAUP","MULTI LINKS","TORRENT"],
    FitGirl:["DataNodes","FuckingFast","MultiUpload","1337x","magnet",".torrent file only","RuTor","magnet","Tapochek.net"]
};

function createFlyout(label,key,items,stars=[]){
    const container=$("#category_block"); if(!container) return;
    const val = key==="platform"? platform : sdl;
    const div=document.createElement("div");
    div.className="menu"; div.dataset.type=key;
    div.innerHTML=`<div class="menu-btn">${label}: ${val}</div><div class="flyout">${items.map(i=>`<a href="#" data-value="${i}">${stars.includes(i)?"⭐"+i:i}</a>`).join("")}</div>`;
    container.insertAdjacentElement("beforebegin",div);
    const btn = div.querySelector(".menu-btn");
    div.querySelectorAll("a").forEach(a=>a.addEventListener("click", e=>{
        e.preventDefault();
        const v=a.dataset.value;
        key==="platform" ? setPlatform(v) : setSourceForPlatform(v);
        btn.textContent=`${label}: ${v}`;
        updateDownloadLink(true);
    }));
}
function renderSourceFlyout(){
    $$('.menu[data-type="source"]').forEach(n=>n.remove());
    const container=$("#category_block"); if(!container) return;
    const list = sourcesByPlatform[platform]||[];
    const val = platformData[platform]?.source||defaultData[platform].source||list[0];
    GM_setValue("platformData",platformData);
    const div=document.createElement("div");
    div.className="menu"; div.dataset.type="source";
    div.innerHTML=`<div class="menu-btn">${sourceLabel}: ${val}</div><div class="flyout">${list.map(i=>`<a href="#" data-value="${i}">${i}</a>`).join("")}</div>`;
    container.insertAdjacentElement("beforebegin",div);
    const btn=div.querySelector(".menu-btn");
    div.querySelectorAll("a").forEach(a=>a.addEventListener("click", e=>{
        e.preventDefault();
        setSourceForPlatform(a.dataset.value);
        btn.textContent=`${sourceLabel}: ${a.dataset.value}`;
        updateDownloadLink(true);
    }));
}

// ---------- Update DL ----------
const updateDownloadLink=async(force=false)=>{
    const dl=$("#add314"); if(!dl) return;
    if(!force && !document.hasFocus()) return;
    if(!gameNameCache) await fetchGameNameIfNeeded();
    if(!gameNameCache) return;
    applyLink(dl);
};

// ---------- Multijoueur bouton ----------
const createMultiButton=async()=>{
 const FixGameSet=new Set(["slime rancher 2"]);
 const slug=await fetchGameNameIfNeeded();
 if(!slug) return;
 const norm=slug.replace(/-/g," ").normalize("NFD").replace(/[\u0300-\u036f\u00A9\u00AE\u2122\u2120]/g,"").trim().toLowerCase();
 const q=norm.replace(/[\\/\s]+/g,"+");
 if(!(document.querySelector(".game_area_details_specs_ctn[href*='category2=3']:is([href*='6'],[href*='8'],[href*='9'])")||!FixGameSet.has(norm))) return;
 if($("#add314r") ) return;
 const url=`https://online-fix.me/index.php?do=search&subaction=search&story=${q}`;
 const doc=await gmFetchHTML(url,10000);
 if(!doc) return;
 const hit=[...doc.querySelectorAll(".article")].map(a=>{
  const h2=a.querySelector("h2.title");
  if(!h2) return null;
  const raw=h2.innerText.replace(/ по сети/gi,"");
  const title=raw.normalize("NFD").replace(/[\u0300-\u036f\u00A9\u00AE\u2122\u2120]/g,"").trim().toLowerCase();
  const link=a.querySelector("a.big-link")?.href||a.querySelector("a.img")?.href||h2.closest("a")?.href||null;
  return title===norm&&link?link:null;
 }).filter(Boolean)[0];
 if(!hit) return;
 const base=$("#add314");
 if(!base) return;
 base.insertAdjacentHTML("afterend",`<a id="add314r" class="btn_green_steamui btn_medium" href="${hit}" target="_blank"><span>${multiLabel}</span></a>`);
};

// ---------- UI init ----------
const initUI=()=>{
    const wrap=$('.game_area_purchase_game_wrapper:not(#demoGameBtn) > * > .game_purchase_action');
    if(!wrap||$('.game_area_comingsoon,.game_area_bubble')) return;
    if($(".menu-container")) return;

    const cont=document.createElement("div"); cont.className="menu-container"; document.body.appendChild(cont);
    createFlyout("🌐","platform",["IGG","Skidrow","FitGirl"],["IGG","Skidrow"]);
    renderSourceFlyout();

    const priceEl=$("[class^='discount_original_price'],[data-price-final]");
    const price=(priceEl?.innerText?.trim())||"";
    const finalPrice = price?`0,--${price.slice(-1)}`:"0,00";
    wrap.innerHTML=`
    <div class="game_purchase_action_bg">
        <div class="discount_block game_purchase_discount" data-discount="100">
            <div class="discount_pct">-100%</div>
            <div class="discount_prices">
                <div class="discount_original_price">${price}</div>
                <div class="discount_final_price">${finalPrice}</div>
            </div>
        </div>
        <div class="btn_addtocart">
            <a id="add314" class="btn_green_steamui btn_medium" href="#" target="_blank"><span>${dlLabel}</span></a>
        </div>
    </div>`;

    updateDownloadLink(true);
    createMultiButton();
};

// ---------- SPA observe ----------
const observer = new MutationObserver(()=>{ if($('.game_area_purchase_game_wrapper')) initUI(); });
const target = $('#appMountPoint') || document.documentElement;
observer.observe(target,{ childList:true, subtree:true });
initUI();

// ---------- IGG auto DL ----------
if(/igg-games.+download\.html\?cdl/i.test(location.href)){
    document.title="⏳"+document.title;
    const temp=6000;
    const tryDownload=()=>{
        $$("b.uk-heading-bullet").forEach(el=>{
            if(el.innerText.replace(/Link\s*:?/i,"").includes(sdl)){
                const links=el.parentElement.querySelectorAll("a[href]");
                let i=0;
                const open=()=>{if(i<links.length){links[i++].click();setTimeout(open,temp);}else setTimeout(()=>window.close(),2000);};
                open();
            }
        });
    };
    tryDownload();
}

// ---------- Online-Fix auto-translate ----------
if(location.hostname.includes("online-fix.me")){
    const lang = navigator.language.split("-")[0];
    const applyTranslate = () => {
        const sel = document.querySelector(".goog-te-combo");
        if(!sel) return false;
        sel.value = lang;
        sel.dispatchEvent(new Event("change"));
        return true;
    };
    const t = setInterval(() => { if(applyTranslate()) clearInterval(t); }, 300);
    document.addEventListener("DOMContentLoaded", applyTranslate);
}

})();