MWI Simulator Auto Import

Tools for Milky Way Idle. Automatically imports set group settings from URL parameters.

当前为 2025-06-02 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         MWI Simulator Auto Import
// @namespace    http://tampermonkey.net/
// @version      0.1.0
// @description  Tools for Milky Way Idle. Automatically imports set group settings from URL parameters.
// @author       AyajiLin
// @match        https://amvoidguy.github.io/MWICombatSimulatorTest/*
// @match        https://shykai.github.io/MWICombatSimulatorTest/dist/*
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @connect      api.textdb.online
// @license      MIT
// ==/UserScript==

unsafeWindow.isAlertEnabled = true;

(function () {
    'use strict';

    /////////////////////
    //                 //
    //    Utilities    //
    //                 //
    /////////////////////
    // Function to create and show a floating message
    function showFloatingMessage(message, options = {}) {
        const messageDiv = document.createElement('div');
        messageDiv.style.cssText = `
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background-color: ${options.backgroundColor || 'rgba(0, 0, 0, 0.8)'};
            color: white;
            padding: 10px 20px;
            border-radius: 5px;
            z-index: 9999;
            font-family: Arial, sans-serif;
            font-size: 14px;
            transition: opacity 0.3s ease-in-out;
        `;
        messageDiv.textContent = message;
        document.body.appendChild(messageDiv);

        // Auto-remove after delay (default: 3 seconds)
        const duration = options.duration || 3000;
        if (duration > 0) {
            setTimeout(() => {
                messageDiv.style.opacity = '0';
                setTimeout(() => messageDiv.remove(), 300); // Match the CSS transition
            }, duration);
        }

        
        return messageDiv;
    }

    // Function to show error message
    function showErrorMessage(message, duration = 3000) {
        return showFloatingMessage(message, {
            backgroundColor: 'rgba(220, 53, 69, 0.9)', // Bootstrap danger color
            duration: duration
        });
    }

    // Function to show success message
    function showSuccessMessage(message, duration = 3000) {
        return showFloatingMessage(message, {
            backgroundColor: 'rgba(25, 135, 84, 0.9)', // Bootstrap success color
            duration: duration
        });
    }

    function clickGetPriceButton() {
        const getPriceButton = document.querySelector(`button#buttonGetPrices`);
        if (getPriceButton) {
            console.log("Click getPriceButton");
            getPriceButton.click();
        }
    }

    async function getGroupJson() {
        // Temporary disable alert
        unsafeWindow.isAlertEnabled = false;
        document.getElementById('buttonExportSet').click();
        // Get json from clipboard
        const json = await navigator.clipboard.readText().finally(() => {
            unsafeWindow.isAlertEnabled = true;
        });
        return json;
    }

    //////////////////////
    //                  //
    //    TextDB API    //
    //                  //
    //////////////////////
    // Generate a random TextDB key (20-40 characters long)
    function generateTextDBKey() {
        const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_';
        const keyLength = 20 + Math.floor(Math.random() * 11); // Random length between 20 and 30
        let result = '';
        const randomValues = new Uint32Array(keyLength);
        window.crypto.getRandomValues(randomValues);
        
        for (let i = 0; i < keyLength; i++) {
            result += chars[randomValues[i] % chars.length];
        }
        return result;
    }

    async function save2TextDB(text) {
        // Generate a random key for TextDB
        let key = generateTextDBKey();
        console.log('Generated TextDB key:', key);

        // Encode the parameters for x-www-form-urlencoded
        const params = new URLSearchParams();
        params.append('key', key);
        params.append('value', text);

        // Check if the key already exists
        const checkResponse = await new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://api.textdb.online/${encodeURIComponent(key)}`,
                onload: resolve,
                onerror: (error) => resolve({ status: 500, responseText: '' })
            });
        });
        
        if (checkResponse.status === 200 && checkResponse.responseText) {
            console.log('Key already exists, generating a new one...');
            key = generateTextDBKey();
            params.set('key', key);
        }
        
        // Save text to textdb.online
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: 'https://api.textdb.online/update/',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                data: params.toString(),
                onload: function(response) {
                    try {
                        const result = JSON.parse(response.responseText);
                        if (response.status === 200 && result.status === 1) {
                            console.log('Text saved to TextDB successfully!', result);
                            resolve({
                                success: true,
                                key: result.data.key,
                                url: result.data.url,
                                reqId: result.req_id
                            });
                        } else {
                            console.error('Failed to save to TextDB:', result);
                            reject(new Error(`API Error: ${result.message || 'Unknown error'}`));
                        }
                    } catch (error) {
                        console.error('Error parsing API response:', error);
                        reject(new Error('Invalid response from TextDB API'));
                    }
                },
                onerror: function(error) {
                    console.error('Error saving to TextDB:', error);
                    reject(error);
                }
            });
        });
    }

    /////////////////
    //             //
    //    Share    //
    //             //
    /////////////////
    function showShareDialog() {
        const dialog = document.getElementById('shareModal');
        if (dialog) {
            dialog.style.display = "block";
            dialog.className = "modal show";
            dialog.ariaModal = "true";
            dialog.role = "dialog";
            dialog.removeAttribute("aria-hidden");
            document.body.classList.add("modal-open");
            document.body.style.overflow = "hidden";
            const modalBackdrop = document.createElement('div');
            modalBackdrop.className = "modal-backdrop show";
            document.body.appendChild(modalBackdrop);
        } else {
            console.error("Dialog not found");
        }
    }

    function hideShareDialog() {
        const dialog = document.getElementById('shareModal');
        if (dialog) {
            dialog.style.display = "none";
            dialog.className = "modal";
            dialog.removeAttribute("aria-modal");
            dialog.removeAttribute("role");
            document.body.classList.remove("modal-open");
            document.body.style.overflow = "";
            const modalBackdrop = document.querySelector('.modal-backdrop');
            if (modalBackdrop) {
                modalBackdrop.remove();
            }
        } else {
            console.error("Share Dialog not found");
        }
    }

    async function share2TextDB() {
        try {
            const json = await getGroupJson();
            const response = await save2TextDB(json);
            console.log('Text saved to TextDB successfully!', response);
            
            // Get the current page's base URL (without query parameters)
            const baseURL = window.location.href.split('?')[0];
            const shareURL = `${baseURL}?textdb=${response.key}`;
            
            navigator.clipboard.writeText(shareURL)
                .then(() => {
                    console.log('Share URL copied to clipboard!', shareURL);
                    showSuccessMessage('Share URL copied to clipboard!');
                })
                .catch(error => {
                    console.error('Error copying URL to clipboard:', error);
                    showErrorMessage('Failed to copy share URL to clipboard');
                });
        } catch (error) {
            console.error('Error sharing to TextDB:', error);
            showErrorMessage(error.message || 'Failed to share to TextDB');
        } finally {
            hideShareDialog();
        }
    }


    function addShareDialog() {
        const dialogHtml = `
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title">Share / 分享</h5>
                        <button type="button" class="btn-close" data-bs-dismiss="modal" id="buttonCloseShare1"></button>
                    </div>
                    <div class="modal-body">
                        <div class="container">
                            <div class="row justify-content-center">
                                <div class="col-md-auto">
                                    <button type="button" class="btn btn-primary" id="buttonShareTextDB">TextDB</button>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="buttonCloseShare2">关闭</button>
                    </div>
                </div>
            </div>
        `;
        const dialogDiv = document.createElement('div');
        dialogDiv.className = "modal";
        dialogDiv.id = "shareModal";
        dialogDiv.tabIndex = "-1";
        dialogDiv.style.display = "none";
        dialogDiv.ariaHidden = "true";
        dialogDiv.innerHTML = dialogHtml;
        const targetDiv = document.getElementById('houseRoomsModal');
        targetDiv.parentNode.insertBefore(dialogDiv, targetDiv.nextSibling);
        // Add `Share` button
        const button = document.createElement('button');
        button.id = "buttonShare";
        button.className = "btn btn-primary";
        button.type = "button";
        button.textContent = "Share/分享";
        button.onclick = showShareDialog;
        const buttonDiv = document.createElement('div');
        buttonDiv.className = "col-md-auto";
        buttonDiv.appendChild(button);
        const targetButton = document.getElementById('buttonImportExport');
        targetButton.parentNode.parentNode.insertBefore(buttonDiv, targetButton.parentNode.nextSibling);
        document.getElementById('buttonCloseShare1').onclick = hideShareDialog;
        document.getElementById('buttonCloseShare2').onclick = hideShareDialog;
        // Bind share logics
        document.getElementById('buttonShareTextDB').onclick = share2TextDB;
    }

    //////////////////
    //              //
    //    Import    //
    //              //
    //////////////////
    // Function that runs when the page is fully loaded
    // This function is used to automatically import the set group combat all
    function onPageLoadForAutoImport() {
        // parse the url for URL parameters
        const urlParams = new URLSearchParams(window.location.search);
        // get url parameter "textdb" if exists
        const textdbID = urlParams.get('textdb');
        // get the import input element
        const importInputElem = document.querySelector(`input#inputSetGroupCombatAll`);
        if (importInputElem == null) {
            return;
        }
        if (textdbID) {
            // Fetch text from textdb.online using the token
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://api.textdb.online/${textdbID}`,
                onload: function(response) {
                    if (response.status === 200) {
                        if (response.responseText) {
                            importInputElem.value = response.responseText;
                            document.querySelector(`button#buttonImportSet`).click();
                            showSuccessMessage('Settings loaded successfully!');
                            clickGetPriceButton();
                        } else {
                            showErrorMessage('No settings found!');
                        }
                    } else {
                        showErrorMessage('Error loading settings!');
                    }
                },
                onerror: function(error) {
                    console.error('Error fetching from textdb.online:', error);
                    showErrorMessage('Error loading settings!');
                }
            });
        }
    }

    // Add event listener for page load
    window.addEventListener('load', onPageLoadForAutoImport);
    // Add Share button
    addShareDialog();
    // Hook alert function
    unsafeWindow.alert = function(msg) {
        console.log("[alert]", msg);
        if (unsafeWindow.isAlertEnabled) {
            showFloatingMessage(msg);
        }
    };

})();