MWI Simulator Auto Import

Tools for Milky Way Idle Combat Simulator. Automatically imports set group settings from URL parameters. Also provides a share feature to share the current settings to online storage.

安裝腳本?
作者推薦腳本

您可能也會喜歡 Real Profit Calculator

安裝腳本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         MWI Simulator Auto Import
// @namespace    http://tampermonkey.net/
// @version      0.1.1
// @description  Tools for Milky Way Idle Combat Simulator. Automatically imports set group settings from URL parameters. Also provides a share feature to share the current settings to online storage.
// @homepage     https://github.com/leonardodalinky/MWI-Simulator-Auto-Import
// @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
// @run-at       document-end
// ==/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.querySelector(`a#group-combat-tab`).click();
        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    //
    //             //
    /////////////////
    var shareURL = "";

    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");
        }
    }

    function enableCopyButton() {
        const button = document.getElementById('buttonCopyShareURL');
        if (button) {
            button.disabled = false;
        }
    }

    function copyShareURL() {
        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');
            });
    }

    function setShareURLText() {
        // Set share URL textbox value and scroll to the end
        const input = document.getElementById('shareURLText');
        if (input) {
            input.value = shareURL;
            // Scroll to the end of the input
            input.scrollLeft = input.scrollWidth;
            // Set selection to the end (optional, shows cursor at end)
            input.selectionStart = input.selectionEnd = shareURL.length;
        }
    }

    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];
            shareURL = `${baseURL}?textdb=${response.key}`;
            showSuccessMessage('Text saved to TextDB successfully!');
            setShareURLText();
            enableCopyButton();
        } catch (error) {
            console.error('Error sharing to TextDB:', error);
            showErrorMessage(error.message || 'Failed to share to TextDB');
        }
    }


    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 mb-3">
                                <div class="col-12">
                                    <div class="input-group">
                                        <input type="text" class="form-control" id="shareURLText" value="Generated Share URL To Be Here..." readonly>
                                    </div>
                                </div>
                            </div>
                            <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-success" data-bs-dismiss="modal" id="buttonCopyShareURL" disabled>Copy URL/复制链接</button>
                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="buttonCloseShare2">Close/关闭</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;
        document.getElementById('buttonCopyShareURL').onclick = copyShareURL;
    }

    //////////////////
    //              //
    //    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) {
                            document.querySelector(`a#group-combat-tab`).click();
                            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);
        }
    };

})();