NovelAI 自定义连点器

自定义点击间隔以及插入随机值,[重复Seed/消耗Anlas]防呆,禁用Generate Variations,请勿滥用,频率请自行斟酌,不负责高频率使用后果,粗制滥造还望海涵。少代码部分参考:LigHT。

// ==UserScript==
// @name         NovelAI 自定义连点器
// @namespace    https://novelai.net
// @match        https://novelai.net/image
// @icon         https://novelai.net/_next/static/media/goose_blue.1580a990.svg
// @license      MIT
// @version      2.1
// @author       Takoro
// @description  自定义点击间隔以及插入随机值,[重复Seed/消耗Anlas]防呆,禁用Generate Variations,请勿滥用,频率请自行斟酌,不负责高频率使用后果,粗制滥造还望海涵。少代码部分参考:LigHT。
// ==/UserScript==

console.log("NovelAI 自定义连点器 ver2.1");

// 目标按钮class
var button_class;

var loopTime = 6000;
var randomTime = 1000;
var tmpTime;
var anlasConsume;

// 计时器
var interval;

// 位移坐标计算
var initX, initY, containerX, containerY;

// xpath路径
// 按钮 /html/body/div[1]/div[2]/div[4]/div[1]/div[5]/button
// 按钮内div /html/body/div[1]/div[2]/div[4]/div[1]/div[5]/button/div

var xpathExpression =
    "/html/body/div[1]/div[2]/div[4]/div[1]/div[5]/button/div";
var anlasXpath =
    "/html/body/div[1]/div[2]/div[4]/div[1]/div[5]/button/div/div[1]/span";
var seedXpath =
    "/html/body/div[1]/div[2]/div[4]/div[1]/div[5]/div/div/div/div[3]/button/span";

// 目标元素验证
var targetElement;

function resetSeed() {
    let seedSpan = document.evaluate(
        seedXpath,
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
    ).singleNodeValue;
    let event = new MouseEvent("click", {
        bubbles: true,
        cancelable: true,
    });
    seedSpan.dispatchEvent(event);
}

function elmClick(elmXpath) {
    let button = document.evaluate(
        elmXpath,
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
    ).singleNodeValue;

    if(button){
        let event = new MouseEvent("click", {
            bubbles: true,
            cancelable: true,
        });
        button.dispatchEvent(event);}
    else{
        //     console.log("元素未找到!");
    }
}

function findingButton() {
    let result = document.evaluate(
        xpathExpression,
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
    );
    targetElement = result.singleNodeValue;
}

function findingAnlas() {
    let result = document.evaluate(
        anlasXpath,
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
    );
    anlasConsume = result.singleNodeValue.innerText;
    if (anlasConsume > 0) {
        let continueExecution = confirm("你要消耗Anlas进行瑟瑟吗?");
        if (!continueExecution) {
            return false;
        }
    } else {
        // console.log("未检测到Anlas,默认return ture");
    }
    // console.log("Anlas消耗:" + anlasConsume);
    return true;
}

// 计时循环查找元素,等待页面加载完成
var intervalId = setInterval(function () {
    findingButton();
    if (targetElement) {
        clearInterval(intervalId);
        button_class = targetElement.getAttribute("class");
        console.log("获取到的随机 class 名为:" + button_class);
        createComponent();
    } else {
        console.log("未找到符合条件的元素");
    }
}, 3000);

function createComponent() {
    var container = document.createElement("div");
    container.style.height = "34px";
    container.style.position = "fixed";
    container.style.display = "flex";
    container.style.justifyContent = "center";
    container.style.alignItems = "center";
    container.style.bottom = "90px";
    container.style.left = "50%";
    container.style.transform = "translateX(-50%)";
    container.style.userSelect = "none";
    container.style.zIndex = "999";
    document.body.appendChild(container);

    var button = document.createElement("button");
    button.innerHTML = "Start";
    button.style.backgroundColor = "rgb(34, 37, 63)";
    button.style.color = "rgb(245 243 194)";
    button.style.border = "0.1px solid rgb(245 243 194)";
    button.style.height = "34px";
    button.style.paddingLeft = "10px";
    button.style.paddingRight = "10px";
    button.style.borderRadius = "3px";
    button.style.fontFamily = "Source Sans Pro";
    button.style.fontSize = "15px";
    button.style.userSelect = "none";
    button.style.cursor = "pointer";
    container.appendChild(button);

    var customGenerate = document.createElement("button");
    customGenerate.title = "按下后,清除图生图和重绘,再生成图像同时清除Seed;可以用作单次生成键使用。";
    customGenerate.style.height = "34px";
    customGenerate.style.width = "34px";
    customGenerate.innerHTML = "↺";
    customGenerate.style.color = "rgb(245 243 194)";
    customGenerate.style.backgroundSize = "14px 14px";
    customGenerate.style.backgroundRepeat = "no-repeat";
    customGenerate.style.backgroundPosition = "center";
    customGenerate.style.backgroundColor = "rgb(34, 37, 63)";
    customGenerate.style.border = "0.1px solid rgb(245, 243, 194)";
    customGenerate.style.borderRadius = "3px";
    customGenerate.style.userSelect = "none";
    customGenerate.style.cursor = "pointer";
    container.appendChild(customGenerate);

    var input = document.createElement("input");
    input.type = "number";
    input.placeholder = "间隔: " + parseInt(loopTime / 1000) + "s";
    input.style.width = "70px";
    input.style.height = "34px";
    input.style.padding = "6px 6px 5px 6px";
    input.style.backgroundColor = "rgb(34, 37, 63)";
    input.style.userSelect = "none";
    container.appendChild(input);

    var random = document.createElement("input");
    random.type = "number";
    random.placeholder = "± " + parseInt(randomTime / 1000) + "s";
    random.style.width = "35px";
    random.style.height = "34px";
    random.style.padding = "6px 1px 5px 6px";
    random.style.backgroundColor = "rgb(34, 37, 63)";
    random.style.userSelect = "none";
    container.appendChild(random);

    var buttonGv = document.createElement("button");
    buttonGv.title = "点击后可以禁用Generate Variations,再次点击启用。";
    buttonGv.style.height = "34px";
    buttonGv.style.width = "34px";
    buttonGv.style.backgroundImage =
        'url("/_next/static/media/variations.d35c8a3a.svg")';
    buttonGv.style.backgroundSize = "14px 14px";
    buttonGv.style.backgroundRepeat = "no-repeat";
    buttonGv.style.backgroundPosition = "center";
    buttonGv.style.backgroundColor = "rgb(34, 37, 63)";
    buttonGv.style.border = "0.1px solid rgb(245, 243, 194)";
    buttonGv.style.borderRadius = "3px";
    buttonGv.style.userSelect = "none";
    buttonGv.style.cursor = "pointer";
    container.appendChild(buttonGv);

    var mover = document.createElement("button");
    mover.innerHTML = "↔";
    mover.title = "长按此元素拖动脚本窗口移动。";
    mover.style.display = "flex";
    mover.style.alignItems = "center";
    mover.style.backgroundColor = "rgb(34, 37, 63)";
    mover.style.color = "rgb(245 243 194)";
    mover.style.border = "0.1px solid rgb(245 243 194)";
    mover.style.height = "34px";
    mover.style.paddingLeft = "10px";
    mover.style.paddingRight = "10px";
    mover.style.borderRadius = "3px";
    mover.style.fontFamily = "Source Sans Pro";
    mover.style.fontSize = "20px";
    mover.style.userSelect = "none";
    mover.style.cursor = "pointer";
    container.appendChild(mover);

    mover.addEventListener("mousedown", function (e) {
        initX = e.pageX;
        initY = e.pageY;
        containerX = container.offsetLeft;
        containerY = container.offsetTop;
        document.addEventListener("mousemove", mouseMoveHandler);
    });

    function mouseMoveHandler(e) {
        var moveX = e.pageX - initX;
        var moveY = e.pageY - initY;
        container.style.left = containerX + moveX + "px";
        container.style.top = containerY + moveY + "px";
    }

    document.addEventListener("mouseup", function () {
        document.removeEventListener("mousemove", mouseMoveHandler);
    });

    function clickElement() {
        if (getRandomInt(0, 9) % 2 == 0) {
            tmpTime = loopTime + getRandomInt(0, randomTime);
        } else {
            tmpTime = loopTime - getRandomInt(0, randomTime);
        }
        // console.log("下一次点击间隔为:" + tmpTime / 1000);
        var target = document.getElementsByClassName(button_class)[0];
        let clickevent = new MouseEvent("click", {
            bubbles: true,
            cancelable: true,
        });
        target.dispatchEvent(clickevent);
        // 在运行一次后清除seed
        resetSeed();

        clearInterval(interval);
        interval = null;
        interval = setInterval(clickElement, tmpTime);
        // console.log("计时器:" + interval);
    }

    button.onclick = function () {
        if (interval) {
            clearInterval(interval);
            interval = null;
            // console.log("计时器销毁:" + interval);
            button.innerHTML = "Start";
        } else {
            if (findingAnlas()) {
                clickElement();
                // console.log("启动计时器:" + tmpTime);
                button.innerHTML = "Pause";
            }
        }
    };

    // gv = Generate Variations
    var disableGv = true;

    setInterval(function () {
        let gvXpath =
            "/html/body/div[1]/div[2]/div[4]/div[2]/div[2]/div[1]/div/div/div/div[2]/button";
        let gvButton = document.evaluate(
            gvXpath,
            document,
            null,
            XPathResult.FIRST_ORDERED_NODE_TYPE,
            null
        ).singleNodeValue;

        if (gvButton !== null) {
            if (disableGv) {
                gvButton.disabled = true;
                buttonGv.style.border = "none";
            } else {
                gvButton.disabled = false;
                buttonGv.style.border = "0.1px solid rgb(245, 243, 194)";
            }
        }
    }, 1000);

    customGenerate.onclick = function () {
        let i2iXpath1 =
            " /html/body/div[1]/div[2]/div[4]/div[1]/div[3]/div[2]/div/div[5]/div/div/div[1]/div[2]/div/button[2]";
        let i2iXpath =
            "/html/body/div[1]/div[2]/div[4]/div[1]/div[3]/div[2]/div/div[3]/div/div[1]/div[2]/div/button[2]";
        let inpanitXpath1 =
            "/html/body/div[1]/div[2]/div[4]/div[1]/div[3]/div[2]/div/div[5]/div/div[1]/div[2]/div/button[2]";
        let inpanitXpath =
            "/html/body/div[1]/div[2]/div[4]/div[1]/div[3]/div[2]/div/div[3]/div/div/div[1]/div[2]/div/button[2]";

        if(findingAnlas()){
            elmClick(i2iXpath);
            elmClick(inpanitXpath);
            elmClick(i2iXpath1);
            elmClick(inpanitXpath1);

            setTimeout(function() {
                elmClick(xpathExpression);
            }, 600);
            setTimeout(function() {
                resetSeed();
            }, 1000);
        }

    };

    buttonGv.onclick = function () {
        let gvXpath =
            "/html/body/div[1]/div[2]/div[4]/div[2]/div[2]/div[1]/div/div/div/div[2]/button";
        let gvButton = document.evaluate(
            gvXpath,
            document,
            null,
            XPathResult.FIRST_ORDERED_NODE_TYPE,
            null
        ).singleNodeValue;

        if (disableGv) {
            disableGv = false;
            buttonGv.style.border = "0.1px solid rgb(245, 243, 194)";
        } else {
            disableGv = true;
            buttonGv.style.border = "none";
        }
    };

    input.onchange = function () {
        loopTime = parseInt(input.value) * 1000;
        // console.log("设置间隔:" + loopTime);
        input.value = "";
        input.placeholder = "间隔: " + parseInt(loopTime / 1000) + "s";
    };

    random.onchange = function () {
        randomTime = parseInt(random.value) * 1000;
        // console.log("随机间隔:" + randomTime);
        random.value = "";
        random.placeholder = "± " + parseInt(randomTime / 1000) + "s";
    };

    function getRandomInt(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
}