Discord/Shapes - Main Logic

Handling the logic of Rules and Lorebook

当前为 2024-11-24 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Discord/Shapes - Main Logic
// @namespace    http://tampermonkey.net/
// @version      1.6
// @description  Handling the logic of Rules and Lorebook
// @author       Vishanka
// @match        https://discord.com/channels/*
// @grant        unsafeWindow
// @run-at document-idle
// ==/UserScript==

(function() {
    'use strict';

    // Function to check localStorage and reload if not ready
    function checkLocalStorageAndReload() {
        try {
            if (localStorage.length > 0) {
                console.log("LocalStorage has items. Proceeding with script...");
                initializeScript();
            } else {
                console.warn("LocalStorage is empty. Reloading page...");
                setTimeout(() => {
                    location.reload();
                }, 5000); // Wait 5 seconds before reloading
            }
        } catch (error) {
            console.error("Error accessing localStorage:", error);
            setTimeout(() => {
                location.reload();
            }, 5000); // Wait 5 seconds before reloading
        }
    }

    // Initial check for localStorage existence
    checkLocalStorageAndReload();

    function initializeScript() {
        // Retrieve settings from localStorage or set default values
        let enterKeyDisabled = JSON.parse(localStorage.getItem('enterKeyDisabled')) || false;
        let customRuleEnabled = JSON.parse(localStorage.getItem('customRuleEnabled')) || true;
        let scanForKeywordsEnabled = JSON.parse(localStorage.getItem('scanForKeywordsEnabled')) || true;

        // Create and add the UI button
 /*       window.uiButton = document.createElement('button');
        uiButton.innerHTML = 'Toggle Settings';
 //       uiButton.style.position = 'fixed';
        uiButton.style.bottom = '20px';
        uiButton.style.right = '20px';
        uiButton.style.zIndex = '1000';
        uiButton.style.padding = '10px';
        uiButton.style.backgroundColor = '#5865F2';
        uiButton.style.color = 'white';
        uiButton.style.border = 'none';
        uiButton.style.borderRadius = '5px';
        uiButton.style.cursor = 'pointer';

  //      document.body.appendChild(uiButton);
*/
        // Create the settings window
        unsafeWindow.settingsWindow = document.createElement('div');
   //     settingsWindow.style.position = 'fixed';
        settingsWindow.style.bottom = '60px';
        settingsWindow.style.right = '20px';
        settingsWindow.style.width = '250px';
        settingsWindow.style.padding = '15px';
 //       settingsWindow.style.backgroundColor = '#2f3136';
        settingsWindow.style.color = 'white';
//        settingsWindow.style.border = '1px solid #5865F2';
        settingsWindow.style.borderRadius = '5px';
  //      settingsWindow.style.display = 'none';
        settingsWindow.style.zIndex = '1001';
DCstoragePanel.appendChild(settingsWindow);
        // Custom Rule Checkbox
        const enableCustomRuleCheckbox = document.createElement('input');
        enableCustomRuleCheckbox.type = 'checkbox';
        enableCustomRuleCheckbox.checked = customRuleEnabled;
        enableCustomRuleCheckbox.id = 'enableCustomRuleCheckbox';

        const enableCustomRuleLabel = document.createElement('label');
        enableCustomRuleLabel.htmlFor = 'enableCustomRuleCheckbox';
        enableCustomRuleLabel.innerText = ' Enable Custom Rules';

        // Scan for Keywords Checkbox
        const enableScanForKeywordsCheckbox = document.createElement('input');
        enableScanForKeywordsCheckbox.type = 'checkbox';
        enableScanForKeywordsCheckbox.checked = scanForKeywordsEnabled;
        enableScanForKeywordsCheckbox.id = 'enableScanForKeywordsCheckbox';

        const enableScanForKeywordsLabel = document.createElement('label');
        enableScanForKeywordsLabel.htmlFor = 'enableScanForKeywordsCheckbox';
        enableScanForKeywordsLabel.innerText = ' Enable Lorebook';

        // Append elements to settings window
        settingsWindow.appendChild(enableCustomRuleCheckbox);
        settingsWindow.appendChild(enableCustomRuleLabel);
        settingsWindow.appendChild(document.createElement('br'));
        settingsWindow.appendChild(enableScanForKeywordsCheckbox);
        settingsWindow.appendChild(enableScanForKeywordsLabel);
  //      document.body.appendChild(settingsWindow);

        // Event listener to open/close settings window
/*        uiButton.addEventListener('click', function() {
            settingsWindow.style.display = settingsWindow.style.display === 'none' ? 'block' : 'none';
        });
*/
        // Update customRuleEnabled when checkbox is toggled, and save it in localStorage
        enableCustomRuleCheckbox.addEventListener('change', function() {
            customRuleEnabled = enableCustomRuleCheckbox.checked;
            localStorage.setItem('customRuleEnabled', JSON.stringify(customRuleEnabled));
        });

        // Update scanForKeywordsEnabled when checkbox is toggled, and save it in localStorage
        enableScanForKeywordsCheckbox.addEventListener('change', function() {
            scanForKeywordsEnabled = enableScanForKeywordsCheckbox.checked;
            localStorage.setItem('scanForKeywordsEnabled', JSON.stringify(scanForKeywordsEnabled));
        });

        // Add event listener to handle Enter key behavior
        window.addEventListener('keydown', function(event) {
            if (event.key === 'Enter' && !event.shiftKey && !enterKeyDisabled) {
                event.preventDefault();
                event.stopPropagation();
                event.stopImmediatePropagation();
                console.log('Enter key disabled');
                enterKeyDisabled = true;

                // Execute main handler for Enter key
                handleEnterKey();

                enterKeyDisabled = false;
            }
        }, true); // Use capture mode to intercept the event before Discord's handlers

        // Main function that handles Enter key behavior
        function handleEnterKey() {
            let inputElement = getInputElement();
            if (inputElement) {
                inputElement.focus();
                if (customRuleEnabled) {
                applyCustomRule(inputElement);
            }
                setCursorToEnd(inputElement);
                if (scanForKeywordsEnabled) {
                    scanForKeywords(inputElement);
                }
                sendMessage(inputElement);
                anotherCustomFunction();
            }
        }



// Function to get the correct input element based on the mode
function getInputElement() {
    return document.querySelector('[data-slate-editor="true"]') || document.querySelector('textarea[class*="textArea_"]');
}

// Function to apply custom rules for the input field
function applyCustomRule(inputElement) {
    const customRule = unsafeWindow.customRuleLogic ? unsafeWindow.customRuleLogic.getCurrentText() : '';

    if (inputElement.nodeName === 'TEXTAREA') {
        // For mobile version where input is <textarea>
        const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
        nativeInputValueSetter.call(inputElement, inputElement.value + customRule);

        const inputEvent = new Event('input', {
            bubbles: true,
            cancelable: true,
        });
        inputElement.dispatchEvent(inputEvent);
    } else {
        // For desktop version where input is a Slate editor
        const inputEvent = new InputEvent('beforeinput', {
            bubbles: true,
            cancelable: true,
            inputType: 'insertText',
            data: customRule,
        });
        inputElement.dispatchEvent(inputEvent);
    }
}

// Function to set the cursor position to the end after inserting the text
function setCursorToEnd(inputElement) {
    if (inputElement.nodeName === 'TEXTAREA') {
        inputElement.selectionStart = inputElement.selectionEnd = inputElement.value.length;
    } else {
        const range = document.createRange();
        range.selectNodeContents(inputElement);
        range.collapse(false);
        const selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
    }
}

// Function to send the message (either click send button or simulate Enter key)
function sendMessage(inputElement) {
    let sendButton = document.querySelector('button[aria-label="Nachricht senden"]');
    if (sendButton) {
        sendButton.click();
        console.log('Send button clicked to send message');
    } else {
        // For desktop version, simulate pressing Enter to send the message
        let enterEvent = new KeyboardEvent('keydown', {
            key: 'Enter',
            code: 'Enter',
            keyCode: 13,
            which: 13,
            bubbles: true,
            cancelable: true
        });
        inputElement.dispatchEvent(enterEvent);
        console.log('Enter key simulated to send message');
    }
}

// Example of adding another function
function anotherCustomFunction() {
    console.log('Another custom function executed');
}


// Function to scan for keywords and access local storage
function scanForKeywords(inputElement) {
    const currentProfile = getCurrentProfile();
    if (currentProfile) {
        // Retrieve all messages before iterating through storage keys
        const messageItems = document.querySelectorAll('div[class*="messageContent_"]');
        const relevantMessages = Array.from(messageItems).slice(-15); // Messages -15 to -1
        const lastMessage = Array.from(messageItems).slice(-1); // Last message only

        // Log the messages for debugging purposes even if no keyword is matched
        console.log("Relevant Messages (last 15):", relevantMessages.map(msg => msg.textContent));
        console.log("Last Message:", lastMessage.map(msg => msg.textContent));

        // Track how many entries have been appended
        let appendedCount = 0;
        const maxAppends = 3;

        // Iterate through all localStorage keys that match the profile-lorebook prefix
        Object.keys(localStorage).forEach(storageKey => {
            if (storageKey.startsWith(`${currentProfile}-lorebook:`)) {
                const entryKeys = storageKey.replace(`${currentProfile}-lorebook:`, '').split(',');
                const entryValue = localStorage.getItem(storageKey);

                // Log the entry keys for debugging purposes
                console.log(`Entry Keys: `, entryKeys);

                entryKeys.forEach(entryKey => {
                    if (appendedCount >= maxAppends) return; // Stop if max appends reached

                    // Check input element text for complete word match of keyword
                    const inputText = inputElement.value || inputElement.textContent;

                    // Combine check for keyword in input or in the last message
                    const isKeywordInInput = inputText && new RegExp(`\\b${entryKey}\\b`).test(inputText);
                    const isKeywordInLastMessage = lastMessage.some(message => {
                        const lastMessageText = message.textContent;
                        return new RegExp(`\\b${entryKey}\\b`).test(lastMessageText);
                    });

                    if (isKeywordInInput || isKeywordInLastMessage) {
                        const keywordAlreadyUsed = relevantMessages.some(message => {
                            const messageText = message.textContent;
                            const bracketContent = messageText.match(/\[(.*?)\]/);

                            return bracketContent ? new RegExp(`\\b${entryKey}\\b`).test(bracketContent[1]) : false;
                        });

                        if (!keywordAlreadyUsed) {
                            // Append the entryValue to the input element
                            if (inputElement.nodeName === 'TEXTAREA') {
                                const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
                                nativeInputValueSetter.call(inputElement, inputElement.value + '\n' + entryValue);

                                const inputEvent = new Event('input', {
                                    bubbles: true,
                                    cancelable: true,
                                });
                                inputElement.dispatchEvent(inputEvent);
                            } else {
                                const inputEvent = new InputEvent('beforeinput', {
                                    bubbles: true,
                                    cancelable: true,
                                    inputType: 'insertText',
                                    data: '\n' + entryValue,
                                });
                                inputElement.dispatchEvent(inputEvent);
                            }
                            appendedCount++; // Increment the count
                            console.log(`Keyword '${entryKey}' detected. Appended lorebook entry to the input.`);
                        } else {
                            console.log(`Keyword '${entryKey}' already found in recent bracketed messages. Skipping append.`);
                        }
                    }
                });
            }
        });

        // Log the total number of entries appended
        console.log(`Total lorebook entries appended: ${appendedCount}`);
    }
}

// Function to get the current profile from local storage
function getCurrentProfile() {
    return localStorage.getItem('currentProfile');
}



  }
})();