您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Handling the logic of Rules and Lorebook
当前为
// ==UserScript== // @name Discord/Shapes - Main Logic // @namespace http://tampermonkey.net/ // @version 1.8 // @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); } getRandomEntry(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'); } function getRandomEntry(inputElement) { const selectedProfile = localStorage.getItem('events.selectedProfile'); if (selectedProfile) { let profileEntries = []; const currentHour = new Date().getHours(); for (let key in localStorage) { if (key.startsWith(`events.${selectedProfile}:`)) { const entryData = JSON.parse(localStorage.getItem(key)); const [startHour, endHour] = entryData.timeRange.split('-').map(Number); // Check if current hour is within the specified time range if (currentHour >= startHour && currentHour < endHour) { profileEntries.push({ key, ...entryData }); } } } if (profileEntries.length > 0) { const probability = parseInt(localStorage.getItem('events.probability') || '100', 10); let selectedEntry = null; while (profileEntries.length > 0) { // Randomly select an entry from the available entries const randomIndex = Math.floor(Math.random() * profileEntries.length); const randomEntry = profileEntries[randomIndex]; // Check if the entry passes the individual probability check if (Math.random() * 100 < randomEntry.probability) { selectedEntry = randomEntry; break; } else { // Remove the entry from the list if it fails the probability check profileEntries.splice(randomIndex, 1); } } if (selectedEntry && Math.random() * 100 < probability) { console.log(`Random Entry Selected: ${selectedEntry.value}`); appendToInput(inputElement, selectedEntry.value); // Append the random entry to the input element } } else { console.log('No entries available for the selected profile in the current time range.'); } } else { console.log('No profile selected. Please select a profile to retrieve a random entry.'); } } // Helper function to append text to the input element function appendToInput(inputElement, text) { const lineBreak = '\n'; if (!inputElement) { console.error('Input element not found.'); return; } if (inputElement.nodeName === 'TEXTAREA') { // Mobile: Append text to <textarea> const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set; nativeInputValueSetter.call(inputElement, inputElement.value + `${lineBreak}${text}`); const inputEvent = new Event('input', { bubbles: true, cancelable: true, }); inputElement.dispatchEvent(inputEvent); } else if (inputElement.hasAttribute('data-slate-editor')) { // Desktop: Append text for Slate editor const inputEvent = new InputEvent('beforeinput', { bubbles: true, cancelable: true, inputType: 'insertText', data: `${lineBreak}${text}`, }); inputElement.dispatchEvent(inputEvent); } else { console.error('Unsupported input element type.'); } } // Expose the function to be accessible from other scripts unsafeWindow.getRandomEntry = getRandomEntry; } })();