Greasy Fork

Greasy Fork is available in English.

EPIC白嫖小助手

每1小时检测一次是否有可以白嫖的epic游戏

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        EPIC白嫖小助手
// @description 每1小时检测一次是否有可以白嫖的epic游戏
// @namespace   https://bbs.tampermonkey.net.cn/
// @version     0.1.9
// @author      CodFrm,Cosil
// @grant       GM_xmlhttpRequest
// @grant       GM_notification
// @grant       GM_closeNotification
// @grant       GM_openInTab
// @grant       GM_getValue
// @grant       GM_setValue
// @storageName   find_epic_free_games
// @connect     store-site-backend-static.ak.epicgames.com
// @connect     www.epicgames.com
// @crontab     0 * * * *
// @license     GPLv3
// @match undefined
// ==/UserScript==

let url = "https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions?locale=zh-Hant&country=CN&allowCountries=CN,HK";

function request(option) {
    return new Promise((resolve, reject) => {
        option.onload = (res) => {
            if (res.status != 200) {
                reject();
            }
            resolve(res)
        };
        option.onerror = () => { reject() };
        GM_xmlhttpRequest(option);
    })
}


return new Promise((resolve, reject) => {
    GM_xmlhttpRequest({
        url: url,
        responseType: "json",
        onload: async (resp) => {
            if (resp.status != 200) {
                GM_notification("epic白嫖失败,网站检测错误:" + resp.status);
                resolve();
            } else {
                let games = [];
                let msg = ""
                let elements = resp.response.data.Catalog.searchStore.elements;
                let itemInLibrary = GM_getValue("item_in_library", {});
                console.log("get::item_in_library", itemInLibrary, Object.keys(itemInLibrary).length);
                //超过10个清空存储
                itemInLibrary = Object.keys(itemInLibrary).length > 10 ? {} : itemInLibrary;
                console.log("now_item_in_library", itemInLibrary);
                for (const key in elements) {
                    //本身不免费,现在免费了
                    if (elements[key].price.totalPrice.originalPrice && !elements[key].price.totalPrice.discountPrice) {
                        //活动还在且未购买
                        if (elements[key].status == "ACTIVE" && !Object.keys(itemInLibrary).includes(elements[key].id)) {
                            msg += elements[key].title + "; "
                            let img = "";
                            for (const imgkey in elements[key].keyImages) {
                                if (elements[key].keyImages[imgkey].type == "DieselStoreFrontWide") {
                                    img = elements[key].keyImages[imgkey].url;
                                    break;
                                }
                            }
                            games.push({
                                title: elements[key].title,
                                url: "https://www.epicgames.com/store/zh-Hant/p/" + elements[key].urlSlug,
                                id: elements[key].id,
                                image: img,
                            });
                        }
                    }
                }
                console.log("found_games", games);
                let parser = new DOMParser();
                console.log("req_start");
                await Promise.all(games.map(game => request({ url: game.url }))).then(resArr => {
                    console.log("req_end", resArr);
                    for (let i in resArr) {
                        //已购标识
                        let match = /(?<="diesel.common.button.in_library"\s*:\s*")[^,"]+(?=",)/.exec(resArr[i].responseText);
                        let in_library_ctx = "\error";
                        if (match) {
                            in_library_ctx = match[0];
                        }
                        //购买状态
                        let status = parser.parseFromString(resArr[i].response, "text/html").querySelector("[data-component=PurchaseCTA]")?.innerText;
                        console.log(resArr[i].finalUrl, in_library_ctx, status);
                        if (in_library_ctx === status) {
                            itemInLibrary[games[i].id] = games[i].title;
                        }
                    }
                })
                //更新已购列表
                GM_setValue("item_in_library", itemInLibrary);
                console.log("update_value", GM_getValue("item_in_library"));
                //删选已购买
                games = games.filter(game => !Object.keys(itemInLibrary).includes(game.id));
                if (!games.length) {
                    return resolve();
                }
                console.log(games[0].image);
                GM_notification({
                    title: "今日白嫖名单",
                    text: msg,
                    buttons: [{
                        title: "我知道了"
                    }, { title: "马上去白嫖" }],
                    onclick(id, btn) {
                        if (btn === 1) {
                            for (let key in games) {
                                GM_openInTab(games[key].url);
                            }
                        }
                        GM_closeNotification(id);
                        resolve();
                    },
                    timeout: 10 * 1000,
                    ondone(click, id) {
                        if (!click) {
                            resolve();
                        }
                    }
                });
            }
        },
        onerror() {
            GM_notification("epic白嫖失败,网络异常");
            resolve();
        }
    });
});