Steam🎮-100%

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

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

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

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

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

您需要先安装一个扩展,例如 篡改猴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);
}

})();