FF14 2023年女儿节应援计划批量领取

最终幻想14 2023年 -LITTLE SPARK- 女儿节企划再启 应援计划批量领取

La data de 04-07-2023. Vezi ultima versiune.

/* eslint-disable require-atomic-updates */
// ==UserScript==
// @name         FF14 2023年女儿节应援计划批量领取
// @description  最终幻想14 2023年 -LITTLE SPARK- 女儿节企划再启 应援计划批量领取
// @namespace    AnnAngela
// @match        https://actff1.web.sdo.com/20230430_NEJ23/*
// @version      2023.0.2
// @license      GNU General Public License v3.0 or later
// @compatible   chrome
// @compatible   firefox
// @compatible   opera
// @compatible   safari
// @run-at       document-end
// ==/UserScript==
"use strict";
(async () => {
    const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
    const getUUID = () => "xxxxxxxxxxxxxxxx".replace(/./g, () => (16 * Math.random() | 0).toString(16));
    const button = document.createElement("div");
    const styles = {
        height: "0.53rem",
        left: "0",
        top: "0.3rem",
        cursor: "not-allowed",
        transition: "all 0.3s ease 0s",
        display: "inline-block",
        position: "fixed",
        zIndex: "1",
        background: "rgb(120,84,167)",
        borderRadius: "0 .53rem .53rem 0",
        fontSize: "0.25rem",
        lineHeight: "1",
        padding: ".14rem .265rem 0 .1rem",
        boxSizing: "border-box",
        color: "rgb(255,253,225)",
        fontFamily: "宋体, sans-serif",
        fontWeight: "700",
    };
    for (const [k, v] of Object.entries(styles)) {
        button.style[k] = v;
    }
    document.body.append(button);
    button.innerText = "正在加载可批量领取道具";
    let stage = 0;
    const availableExps = {
        0: [],
        29: [],
        88: [],
    };
    let isPay29 = false, isPay88 = false;
    let targetExp = 0;
    try {
        const response = await (await fetch("https://actff1.web.sdo.com/20230430_NEJ23/Handler/Item/GetMyItemStatus.ashx", {
            headers: {
                "x-requested-with": "XMLHttpRequest",
            },
            body: null,
            method: "POST",
            mode: "cors",
            credentials: "include",
        })).json();
        const { result, vItemStatus, IsPay29, IsPay88, myExp } = response;
        if (result !== "1") {
            throw response;
        }
        console.info("[stage=1] response:", response);
        isPay29 = !!IsPay29;
        isPay88 = !!IsPay88;
        targetExp = myExp;
        for (let i = 0; i < vItemStatus.length; i++) {
            const item = vItemStatus[i];
            if (item.Status === 0) {
                availableExps[item.ItemLevel].push(item.Exp);
            }
        }
        stage = 1;
    } catch (e) {
        console.error("[stage=1]", e);
        button.innerText = "加载可批量领取道具失败(请检查是否登录)";
    }
    if (stage < 1) {
        return;
    }
    const availableItems = {
        0: [],
        29: [],
        88: [],
    };
    const needPurchaseItems = {
        29: [],
        88: [],
    };
    for (const { exp, nq, ncode, hq, hcode, bq, bcode } of window.actConfig.passRet) {
        if (availableExps[0].includes(exp) && /^\d{4,}$/.test(ncode)) {
            availableItems[0].push({ type: "n", name: nq, code: ncode, exp });
        }
        if (availableExps[29].includes(exp) && /^\d{4,}$/.test(hcode)) {
            availableItems[29].push({ type: "h", name: hq, code: hcode, exp });
        }
        if (availableExps[88].includes(exp) && /^\d{4,}$/.test(bcode)) {
            availableItems[88].push({ type: "b", name: bq, code: bcode, exp });
        }
        if (!isPay29 && !!hcode && exp <= targetExp) {
            needPurchaseItems[29].push({ type: "h", name: hq, code: hcode, exp });
        }
        if (!isPay88 && !!bcode && exp <= targetExp) {
            needPurchaseItems[88].push({ type: "b", name: bq, code: bcode, exp });
        }
    }
    const availableItemsCount = Object.values(availableItems).reduce((p, { length }) => p + length, 0);
    const needPurchaseItemsCount = Object.values(needPurchaseItems).reduce((p, { length }) => p + length, 0);
    if (availableItemsCount + needPurchaseItemsCount === 0) {
        button.innerText = "暂无可批量领取道具(乐谱、时装自选请手动领取)";
        return;
    }
    button.style.cursor = "pointer";
    button.innerText = `有${availableItemsCount}个可批量领取道具,点击查看详情`;
    button.addEventListener("click", async () => {
        if (button.style.cursor !== "pointer") {
            return;
        }
        for (const [targetType, targetLevel, targetTypeName] of [
            ["n", 0, "普通"],
            ["h", 29, "黄金"],
            ["b", 88, "白金"],
        ]) {
            const availableItemsForTargetType = availableItems[targetLevel].filter(({ type }) => type === targetType);
            if (targetType === "h" && !isPay29 || targetType === "b" && !isPay88) {
                alert(`您尚未购买${targetTypeName}版偶像应援徽章,${needPurchaseItems[targetLevel].length > 0 ? `无法领取对应${needPurchaseItems[targetLevel].length}个道具` : "当前暂无符合条件对应道具"}。`);
                continue;
            }
            if (availableItemsForTargetType.length === 0) {
                alert(`当前版本:${targetTypeName}\n可领取道具:(无)`);
                continue;
            }
            alert(`当前版本:${targetTypeName}\n可领取道具:${availableItemsForTargetType.map(({ name, exp }) => `[${exp / 100}级] ${name}`).join("、")}`);
            if (!confirm(`您是否批量领取${targetTypeName}版所有可领取道具?\n点击【取消】可取消操作。`) || !confirm(`您真的要批量领取${targetTypeName}版所有可领取道具?\n点击【取消】可取消操作,批量领取流程可在左侧按钮查看。`)) {
                continue;
            }
            button.style.cursor = "not-allowed";
            const success = [], failed = [];
            for (let i = 0; i < availableItemsForTargetType.length; i++) {
                button.innerText = `正在批量领取${targetTypeName}版所有可领取道具:${i}/${availableItemsForTargetType.length}`;
                if (i !== 0) {
                    await sleep(5000);
                }
                const availableItem = availableItemsForTargetType[i];
                try {
                    const response = await (await fetch("https://actff1.web.sdo.com/20230430_NEJ23/Handler/Item/Exchange.ashx", {
                        headers: {
                            "x-requested-with": "XMLHttpRequest",
                            "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
                        },
                        referrer: "https://actff1.web.sdo.com/20230430_NEJ23/index.html?2023/6/28%208:52:44",
                        referrerPolicy: "strict-origin-when-cross-origin",
                        body: new URLSearchParams({
                            ItemExp: availableItem.exp,
                            ItemLevel: targetLevel,
                            ItemCode: availableItem.code,
                            ItemPeathID: getUUID(),
                        }).toString(),
                        method: "POST",
                        mode: "cors",
                        credentials: "include",
                    })).json();
                    if (response.result !== "1") {
                        throw response;
                    }
                    console.info({ targetType, targetLevel, targetTypeName, i, availableItem, response });
                    success.push(availableItem);
                } catch (error) {
                    console.error({ targetType, targetLevel, targetTypeName, i, availableItem, error });
                    failed.push(availableItem);
                }
            }
            button.innerText = `正在批量领取${targetTypeName}版所有可领取道具:${availableItemsForTargetType.length}/${availableItemsForTargetType.length}`;
            await sleep(100);
            alert(`批量领取${targetTypeName}版所有可领取道具结果:\n成功:${success.map(({ name, exp }) => `[${exp / 100}级] ${name}`).join("、") || "(无)"}\n失败:${failed.map(({ name, exp }) => `[${exp / 100}级] ${name}`).join("、") || "(无)"}`);
        }
        if (button.style.cursor !== "pointer") {
            await sleep(100);
            alert("批量领取流程结束,即将刷新页面!");
            await sleep(100);
            location.reload();
        }
    });
})();