您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Various Functions for Shapes.inc for Discord
当前为
// ==UserScript== // @name Discord/Shapes Tools // @namespace https://vishanka.com // @version 1.1 // @description Various Functions for Shapes.inc for Discord // @author Vishanka // @match https://discord.com/channels/* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @license Proprietary // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // ================================================================== LOREBOOK ================================================================== function LorebookScript(){ // Create and add the arrow button to open the storage panel const arrowButton = document.createElement('div'); arrowButton.innerHTML = '〈'; arrowButton.style.position = 'fixed'; arrowButton.style.bottom = '50%'; arrowButton.style.right = '0'; arrowButton.style.padding = '10px'; arrowButton.style.fontSize = '24px'; arrowButton.style.zIndex = '1000'; arrowButton.style.cursor = 'pointer'; arrowButton.style.color = '#B4B4B4'; arrowButton.style.borderRadius = '5px 0 0 5px'; arrowButton.style.transition = 'transform 0.3s ease, right 0.3s ease, background-color 0.1s'; // Toggle panel visibility arrowButton.addEventListener('click', () => { if (DCstoragePanel.style.right === '-250px') { DCstoragePanel.style.right = '0'; arrowButton.style.right = '250px'; arrowButton.style.transform = 'rotate(180deg)'; } else { DCstoragePanel.style.right = '-250px'; arrowButton.style.right = '0'; arrowButton.style.transform = 'rotate(0deg)'; } }); // Create the fancy sliding panel window.DCstoragePanel = document.createElement('div'); DCstoragePanel.style.position = 'fixed'; DCstoragePanel.style.top = '0'; DCstoragePanel.style.right = '-250px'; // Initially hidden DCstoragePanel.style.height = '100%'; DCstoragePanel.style.width = '250px'; DCstoragePanel.style.backgroundColor = '#171717'; DCstoragePanel.style.transition = 'right 0.3s ease'; DCstoragePanel.style.zIndex = '999'; // Create the header above the button const storagePanelHeader = document.createElement('div'); storagePanelHeader.innerText = 'Shapes Tools'; storagePanelHeader.style.margin = '20px'; storagePanelHeader.style.padding = '10px'; storagePanelHeader.style.fontSize = '19px'; storagePanelHeader.style.fontWeight = '550'; storagePanelHeader.style.color = '#ECECEC'; storagePanelHeader.style.textAlign = 'center'; // Create a divider line const dividerLine = document.createElement('div'); dividerLine.style.height = '1px'; dividerLine.style.backgroundColor = '#212121'; dividerLine.style.margin = '10px 20px'; // Manage Lorebook Button window.openLorebookButton = document.createElement('div'); window.openLorebookButton.innerHTML = ` <button id="toggle-lorebook-panel" style=" position: relative; top: 10px; right: 0px; left: 10px; padding: 7px 15px; background: transparent; color: #b0b0b0; border: none; border-radius: 8px; font-size: 16px; text-align: center; cursor: pointer; width: 90%; transition: background-color 0.1s, color 0.1s; z-index: 1001;"> Manage Lorebook </button> `; const lorebookButtonElement = openLorebookButton.querySelector('button'); lorebookButtonElement.onmouseover = () => { lorebookButtonElement.style.backgroundColor = '#212121'; lorebookButtonElement.style.color = '#ffffff'; }; lorebookButtonElement.onmouseout = () => { lorebookButtonElement.style.backgroundColor = 'transparent'; lorebookButtonElement.style.color = '#b0b0b0'; }; // Create the main panel container const lorebookPanel = document.createElement('div'); lorebookPanel.id = 'lorebookManagerPanel'; lorebookPanel.style.position = 'fixed'; lorebookPanel.style.top = '50%'; lorebookPanel.style.left = '50%'; lorebookPanel.style.transform = 'translate(-50%, -50%)'; // Size different for Mobile and Desktop if (window.innerWidth <= 768) { lorebookPanel.style.width = '90%'; lorebookPanel.style.height = '90%'; } else { lorebookPanel.style.width = '800px'; lorebookPanel.style.height = '700px'; } lorebookPanel.style.backgroundColor = '#2F2F2F'; lorebookPanel.style.borderRadius = '20px'; lorebookPanel.style.padding = '10px'; lorebookPanel.style.display = 'none'; lorebookPanel.style.zIndex = '1000'; // Add close button for the panel const closeButton = document.createElement('button'); closeButton.innerText = '✕'; closeButton.style.position = 'absolute'; closeButton.style.borderRadius = '50%'; closeButton.style.color = '#ffffff'; closeButton.style.top = '20px'; closeButton.style.right = '20px'; closeButton.style.backgroundColor = 'transparent'; closeButton.style.cursor = 'pointer'; closeButton.addEventListener('mouseenter', () => { closeButton.style.backgroundColor = '#676767'; }); closeButton.addEventListener('mouseleave', () => { closeButton.style.backgroundColor = 'transparent'; }); // Hide the Lorebook Panel closeButton.addEventListener('click', () => { lorebookPanel.style.display = 'none'; }); // Open the Lorebook Panel openLorebookButton.addEventListener('click', () => { lorebookPanel.style.display = lorebookPanel.style.display === 'none' ? 'block' : 'none'; loadProfileEntries(); }); // Profiles Title const profileslabel = document.createElement('div'); profileslabel.textContent = 'Profiles'; profileslabel.style.color = '#dddddd'; profileslabel.style.fontSize = '14px'; profileslabel.style.marginBottom = '5px'; profileslabel.style.marginLeft = '3px'; profileslabel.style.marginTop = '5px'; profileslabel.style.fontSize = '20px'; profileslabel.style.fontWeight = '550'; // Profile Management Panel const lorebookProfilePanel = document.createElement('div'); lorebookProfilePanel.id = 'lorebookProfilePanel'; lorebookProfilePanel.style.float = 'left'; lorebookProfilePanel.style.width = '20%'; lorebookProfilePanel.style.borderRight = '0.5px solid #444444'; lorebookProfilePanel.style.height = '93%'; // Create the profile list container const profileList = document.createElement('div'); profileList.id = 'profileList'; profileList.style.height = '95%'; profileList.style.color = 'white'; profileList.style.overflowY = 'auto'; // Add Profiles Button const addProfileButton = document.createElement('button'); addProfileButton.innerText = 'Add Profile'; addProfileButton.style.padding = '8px'; addProfileButton.style.border = '0.2px solid #4E4E4E'; addProfileButton.style.backgroundColor = 'transparent'; addProfileButton.style.color = '#fff'; addProfileButton.style.borderRadius = '20px'; addProfileButton.style.width = '90%'; addProfileButton.style.cursor = 'pointer'; // Mouseover Effect for Add Profiles Button addProfileButton.onmouseover = () => { addProfileButton.style.backgroundColor = '#424242'; }; addProfileButton.onmouseout = () => { addProfileButton.style.backgroundColor = 'transparent'; }; addProfileButton.addEventListener('click', () => { const profileName = prompt('Enter profile name:'); if (profileName) { const profileKey = `lorebook.profile:${profileName}`; // Create a list of keys from localStorage that match the prefix 'lorebook.profile:' const existingKeys = Object.keys(localStorage) .filter(key => key.startsWith('lorebook.profile:')) .map(key => key.toLowerCase()); // Convert all keys to lowercase for case-insensitive check if (!existingKeys.includes(profileKey.toLowerCase())) { localStorage.setItem(profileKey, JSON.stringify({})); loadProfiles(); } else { alert('Profile already exists.'); } } }); // Profile Selection Functionality function loadProfiles() { profileList.innerHTML = ''; Object.keys(localStorage).forEach(profileKey => { if (profileKey.startsWith('lorebook.profile:')) { const profileName = profileKey.replace('lorebook.profile:', ''); const profileItem = document.createElement('div'); profileItem.innerText = profileName; profileItem.style.padding = '5px'; profileItem.style.marginBottom = '5px'; profileItem.style.cursor = 'pointer'; profileItem.style.backgroundColor = profileName === getCurrentProfile() ? '#424242' : '#2F2F2F'; profileItem.style.borderRadius = '5px'; profileItem.style.width = '90%'; profileItem.style.position = 'relative'; profileItem.addEventListener('click', () => { setCurrentProfile(profileName); loadProfiles(); loadProfileEntries(); }); const removeButton = document.createElement('button'); removeButton.innerText = '✕'; removeButton.style.position = 'absolute'; removeButton.style.top = '3px'; removeButton.style.right = '10px'; removeButton.style.cursor = 'pointer'; removeButton.style.backgroundColor = 'transparent'; removeButton.style.color = 'white'; removeButton.addEventListener('click', (e) => { e.stopPropagation(); localStorage.removeItem(profileKey); if (profileName === getCurrentProfile()) { setCurrentProfile(null); } loadProfiles(); loadProfileEntries(); }); profileItem.appendChild(removeButton); profileList.appendChild(profileItem); } }); } // Lorebook Entries Title const lorebookEntriesTitle = document.createElement('h3'); lorebookEntriesTitle.innerText = 'Manage Lorebook Entries'; lorebookEntriesTitle.style.fontWeight = 'normal'; lorebookEntriesTitle.style.color = '#ffffff'; lorebookEntriesTitle.style.textAlign = 'left'; lorebookEntriesTitle.style.fontSize = '24px'; lorebookEntriesTitle.style.marginTop = '20px'; lorebookEntriesTitle.style.position = 'relative'; lorebookEntriesTitle.style.marginLeft = '23%'; lorebookEntriesTitle.style.marginBottom = '15px'; lorebookEntriesTitle.style.fontWeight = '550'; // Profile Entries List const profileEntriesList = document.createElement('div'); profileEntriesList.id = 'profileEntriesList'; profileEntriesList.style.marginTop = '20px'; profileEntriesList.style.height = '48%'; // Check if the device is mobile if (window.innerWidth <= 768) { profileEntriesList.style.height = '30%'; } else { profileEntriesList.style.height = '48%'; } profileEntriesList.style.overflowY = 'auto'; // Header for Inputs const entrieslabel = document.createElement('div'); entrieslabel.textContent = 'Enter keys and description:'; entrieslabel.style.color = '#dddddd'; entrieslabel.style.fontSize = '14px'; entrieslabel.style.marginBottom = '5px'; entrieslabel.style.marginTop = '5px'; entrieslabel.style.marginLeft = '23%'; // Create key-value input fields const inputContainer = document.createElement('div'); inputContainer.id = 'inputContainer'; inputContainer.style.marginTop = '10px'; inputContainer.style.display = 'flex'; inputContainer.style.flexDirection = 'column'; inputContainer.style.alignItems = 'center'; inputContainer.style.margin = '0 auto'; const lorebookKeyInput = document.createElement('input'); lorebookKeyInput.type = 'text'; lorebookKeyInput.placeholder = 'Entry Keywords (comma-separated)'; lorebookKeyInput.style.width = '90%'; lorebookKeyInput.style.marginBottom = '5px'; lorebookKeyInput.style.padding = '10px'; lorebookKeyInput.style.border = '1px solid #444444'; lorebookKeyInput.style.borderRadius = '8px'; lorebookKeyInput.style.backgroundColor = '#1e1e1e'; lorebookKeyInput.style.color = '#dddddd'; const lorebookValueInput = document.createElement('textarea'); lorebookValueInput.placeholder = ' '; lorebookValueInput.style.width = '90%'; lorebookValueInput.style.marginBottom = '5px'; lorebookValueInput.style.padding = '10px'; lorebookValueInput.style.border = '1px solid #444444'; lorebookValueInput.style.borderRadius = '8px'; lorebookValueInput.style.backgroundColor = '#1e1e1e'; lorebookValueInput.style.color = '#dddddd'; lorebookValueInput.style.height = '100px'; lorebookValueInput.style.resize = 'vertical'; lorebookValueInput.maxLength = 1000; lorebookValueInput.style.overflow = 'auto'; const charCounter = document.createElement('div'); charCounter.style.color = '#dddddd'; charCounter.style.fontSize = '12px'; charCounter.style.marginTop = '0px'; charCounter.style.marginBottom = '15px'; charCounter.style.textAlign = 'right'; charCounter.style.marginRight = '-87%'; charCounter.style.color = 'grey'; charCounter.textContent = `0/${lorebookValueInput.maxLength}`; // Update the counter as the user types lorebookValueInput.addEventListener('input', () => { charCounter.textContent = `${lorebookValueInput.value.length}/${lorebookValueInput.maxLength}`; }); // Save Entry button, also important for editing const lorebookSaveButton = document.createElement('button'); lorebookSaveButton.innerText = 'Add Entry'; lorebookSaveButton.style.padding = '10px 20px'; lorebookSaveButton.style.border = '0.2px solid #4E4E4E'; lorebookSaveButton.style.backgroundColor = '#2F2F2F'; lorebookSaveButton.style.color = '#fff'; lorebookSaveButton.style.borderRadius = '50px'; lorebookSaveButton.style.cursor = 'pointer'; lorebookSaveButton.style.width = '95%'; // Append all Elements document.body.appendChild(arrowButton); document.body.appendChild(DCstoragePanel); DCstoragePanel.appendChild(storagePanelHeader); DCstoragePanel.appendChild(dividerLine); document.body.appendChild(lorebookPanel); lorebookPanel.appendChild(closeButton); lorebookPanel.appendChild(profileslabel); lorebookPanel.appendChild(lorebookProfilePanel); lorebookProfilePanel.appendChild(profileList); lorebookProfilePanel.appendChild(addProfileButton); lorebookPanel.appendChild(lorebookEntriesTitle); lorebookPanel.appendChild(profileEntriesList); lorebookPanel.appendChild(entrieslabel); lorebookPanel.appendChild(inputContainer); inputContainer.appendChild(lorebookKeyInput); inputContainer.appendChild(lorebookValueInput); inputContainer.appendChild(charCounter); inputContainer.appendChild(lorebookSaveButton); let isEditing = false; let editingKey = ''; lorebookSaveButton.addEventListener('click', () => { const key = lorebookKeyInput.value.trim().toLowerCase(); // Ensure key is always saved as lowercase const value = lorebookValueInput.value; const currentProfile = getCurrentProfile(); if (key && currentProfile) { const profileKey = `${currentProfile}.lorebook:${key}`; const formattedValue = `<[Lorebook: ${key}] ${value}>`; // Check for duplicate keys (case-insensitive) and prevent keys that partially match an existing key const isDuplicateKey = Object.keys(localStorage).some(storageKey => { const normalizedStorageKey = storageKey.toLowerCase(); const normalizedCurrentProfile = currentProfile.toLowerCase(); const currentKey = normalizedStorageKey.replace(`${normalizedCurrentProfile}.lorebook:`.toLowerCase(), ''); return ( (currentKey === key || currentKey.split(',').includes(key)) && (!isEditing || editingKey.toLowerCase() !== currentKey) ); }); // Enforce uniqueness for edits as well if (isDuplicateKey) { alert('The key is already used in an existing entry (case-insensitive). Please use a different key.'); return; } // Remove the old key if editing and key has changed if (isEditing && editingKey.toLowerCase() !== key) { const oldProfileKey = `${currentProfile}.lorebook:${editingKey.toLowerCase()}`; localStorage.removeItem(oldProfileKey); } localStorage.setItem(profileKey, formattedValue); lorebookKeyInput.value = ''; lorebookValueInput.value = ''; isEditing = false; editingKey = ''; loadProfileEntries(); } else { alert('Please select a profile and enter a key.'); } }); function loadProfileEntries() { profileEntriesList.innerHTML = ''; profileEntriesList.style.display = 'flex'; profileEntriesList.style.flexDirection = 'column'; profileEntriesList.style.alignItems = 'center'; profileEntriesList.style.margin = '0 auto'; const currentProfile = getCurrentProfile(); if (currentProfile) { Object.keys(localStorage).forEach(storageKey => { // Normalize both the profile and the storage key for case-insensitive comparison if (storageKey.toLowerCase().startsWith(`${currentProfile.toLowerCase()}.lorebook:`)) { const entryKey = storageKey.replace(new RegExp(`^${currentProfile}\.lorebook:`, 'i'), ''); const entryValue = localStorage.getItem(storageKey); const displayedValue = entryValue.replace(/^<\[Lorebook:.*?\]\s*/, '').replace(/>$/, ''); const entryItem = document.createElement('div'); entryItem.style.padding = '10px'; entryItem.style.marginBottom = '12px'; entryItem.style.borderRadius = '8px'; entryItem.style.backgroundColor = '#424242'; entryItem.style.position = 'relative'; entryItem.style.color = 'white'; entryItem.style.flexDirection = 'column'; entryItem.style.width = '90%'; const keyElement = document.createElement('div'); keyElement.innerText = entryKey; keyElement.style.fontWeight = 'bold'; keyElement.style.marginBottom = '10px'; entryItem.appendChild(keyElement); const valueElement = document.createElement('div'); valueElement.innerText = displayedValue; entryItem.appendChild(valueElement); entryItem.addEventListener('click', () => { lorebookKeyInput.value = entryKey; lorebookValueInput.value = entryValue.replace(/^<\[Lorebook:.*?\]\s*/, '').replace(/>$/, ''); isEditing = true; editingKey = entryKey; }); const removeButton = document.createElement('button'); removeButton.innerText = '✕'; removeButton.style.position = 'absolute'; removeButton.style.top = '10px'; removeButton.style.right = '10px'; removeButton.style.cursor = 'pointer'; removeButton.style.backgroundColor = 'transparent'; removeButton.style.color = 'white'; removeButton.addEventListener('click', (event) => { event.stopPropagation(); localStorage.removeItem(storageKey); loadProfileEntries(); }); entryItem.appendChild(removeButton); profileEntriesList.appendChild(entryItem); } }); } } // Utility functions to manage profiles and local storage function getCurrentProfile() { // Return the current profile name in its original case, but convert to lower case for comparisons const selectedProfile = localStorage.getItem('selectedProfile.lorebook'); return selectedProfile ? selectedProfile : null; } function setCurrentProfile(profileName) { localStorage.setItem('selectedProfile.lorebook', profileName); } loadProfiles(); } // ================================================================ IMPORT/EXPORT =============================================================== function ImportExportScript() { // Create buttons to trigger export and import window.exportButton = document.createElement('div'); exportButton.innerHTML = ` <div style="display: flex; align-items: center; justify-content: center;"> <button style="margin-top: 20px; margin-left: 4px; margin-right: 5px; padding: 7px 51px; font-size: 16px; cursor: pointer; background-color: transparent; color: #b0b0b0; border-radius: 8px; text-align: center; transition: background-color 0.1s, color 0.1s;">Export localStorage</button> </div> `; window.exportButton.onmouseover = () => { exportButton.querySelector('button').style.backgroundColor = '#212121'; exportButton.querySelector('button').style.color = '#ffffff'; }; exportButton.onmouseout = () => { exportButton.querySelector('button').style.backgroundColor = 'transparent'; exportButton.querySelector('button').style.color = '#b0b0b0'; }; //DCstoragePanel.appendChild(exportButton); window.importButton = document.createElement('div'); importButton.innerHTML = ` <div style="display: flex; align-items: center; justify-content: center;"> <button style="margin-top: 20px; margin-left: 4px; margin-right: 5px; padding: 7px 50px; font-size: 16px; cursor: pointer; background-color: transparent; color: #b0b0b0; border-radius: 8px; text-align: center; transition: background-color 0.1s, color 0.1s;">Import localStorage</button> </div> `; importButton.onmouseover = () => { importButton.querySelector('button').style.backgroundColor = '#212121'; importButton.querySelector('button').style.color = '#ffffff'; }; importButton.onmouseout = () => { importButton.querySelector('button').style.backgroundColor = 'transparent'; importButton.querySelector('button').style.color = '#b0b0b0'; }; //DCstoragePanel.appendChild(importButton); // Export specific localStorage entries exportButton.addEventListener('click', () => { const filteredData = {}; for (const key in localStorage) { if (localStorage.hasOwnProperty(key)) { if (key.startsWith('events') || key.includes('lorebook')) { filteredData[key] = localStorage.getItem(key); } } } const data = JSON.stringify(filteredData); const blob = new Blob([data], {type: 'application/json'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'localStorage_filtered.json'; document.body.appendChild(a); a.click(); document.body.removeChild(a); }); // Import localStorage importButton.addEventListener('click', () => { const input = document.createElement('input'); input.type = 'file'; input.accept = 'application/json'; input.addEventListener('change', (event) => { const file = event.target.files[0]; if (!file) { return; } const reader = new FileReader(); reader.onload = (e) => { try { const importedData = JSON.parse(e.target.result); for (const key in importedData) { localStorage.setItem(key, importedData[key]); } alert('localStorage has been successfully imported.'); } catch (err) { alert('Failed to import localStorage: ' + err.message); } }; reader.readAsText(file); }); input.click(); }); } // =================================================================== EVENTS =================================================================== function EventsScript() { window.eventsButton = document.createElement('div'); window.eventsButton.innerHTML = ` <button id="toggle-events-panel" style=" position: relative; top: 10px; right: 0px; left: 10px; padding: 7px 15px; background: transparent; color: #b0b0b0; border: none; border-radius: 8px; font-size: 16px; text-align: center; cursor: pointer; width: 90%; transition: background-color 0.1s, color 0.1s; z-index: 1001;"> Manage Events </button> `; let manageEventsButton = window.eventsButton.querySelector('#toggle-events-panel'); manageEventsButton.onclick = openProfilePanel; const eventsbuttonElement = eventsButton.querySelector('button'); eventsbuttonElement.onmouseover = () => { eventsbuttonElement.style.backgroundColor = '#212121'; eventsbuttonElement.style.color = '#ffffff'; }; eventsbuttonElement.onmouseout = () => { eventsbuttonElement.style.backgroundColor = 'transparent'; eventsbuttonElement.style.color = '#b0b0b0'; }; function openProfilePanel() { if (document.querySelector('#eventsProfilePanel')) { return; } // Create profile management panel const eventsProfilePanel = document.createElement('div'); eventsProfilePanel.id = 'eventsProfilePanel'; eventsProfilePanel.style.position = 'fixed'; eventsProfilePanel.style.top = '50%'; eventsProfilePanel.style.left = '50%'; eventsProfilePanel.style.transform = 'translate(-50%, -50%)'; // Size different for Mobile and Desktop if (window.innerWidth <= 768) { eventsProfilePanel.style.width = '90%'; eventsProfilePanel.style.height = '90%'; } else { eventsProfilePanel.style.width = '800px'; eventsProfilePanel.style.height = '700px'; } //eventsProfilePanel.style.width = '800px'; //eventsProfilePanel.style.height = '700px'; eventsProfilePanel.style.backgroundColor = '#2F2F2F'; eventsProfilePanel.style.color = 'white'; eventsProfilePanel.style.borderRadius = '20px'; eventsProfilePanel.style.padding = '20px'; eventsProfilePanel.style.zIndex = '1000'; eventsProfilePanel.style.display = 'flex'; eventsProfilePanel.style.flexDirection = 'row'; // Create close button for profilePanel const closeButton = document.createElement('button'); closeButton.style.position = 'absolute'; closeButton.style.top = '15px'; closeButton.style.right = '15px'; closeButton.style.width = '30px'; closeButton.style.height = '30px'; closeButton.style.backgroundColor = 'transparent'; closeButton.style.color = '#ffffff'; closeButton.style.border = 'none'; closeButton.style.borderRadius = '50%'; closeButton.style.cursor = 'pointer'; closeButton.style.display = 'flex'; closeButton.style.alignItems = 'center'; closeButton.style.zIndex = '1001'; closeButton.style.justifyContent = 'center'; closeButton.style.transition = 'background-color 0.2s ease'; closeButton.style.boxSizing = 'border-box'; // Create span for the '✕' character const closeIcon = document.createElement('span'); closeIcon.innerText = '✕'; closeIcon.style.fontSize = '16px'; closeIcon.style.position = 'relative'; closeIcon.style.top = '-1px'; // Hover effect closeButton.addEventListener('mouseenter', () => { closeButton.style.backgroundColor = '#676767'; }); closeButton.addEventListener('mouseleave', () => { closeButton.style.backgroundColor = 'transparent'; }); // Close button action closeButton.onclick = function() { document.body.removeChild(eventsProfilePanel); }; // Create profile list container const profileListContainer = document.createElement('div'); profileListContainer.style.flex = '0.50'; profileListContainer.style.marginRight = '20px'; profileListContainer.style.paddingRight = '20px'; profileListContainer.style.display = 'flex'; profileListContainer.style.flexDirection = 'column'; profileListContainer.style.borderRight = '1px solid #444444'; profileListContainer.style.overflowY = 'auto'; profileListContainer.style.maxHeight = '660px'; // Create header for profile list const profileListHeader = document.createElement('h4'); profileListHeader.innerText = 'Profiles'; profileListHeader.style.marginBottom = '10px'; // Create profile list const profileList = document.createElement('ul'); profileList.style.overflowY = 'auto'; profileList.style.height = '600px'; // Create button to add profile const addProfileButton = document.createElement('button'); addProfileButton.innerText = 'Add Profile'; addProfileButton.style.padding = '8px'; addProfileButton.style.border = '0.2px solid #4E4E4E'; addProfileButton.style.backgroundColor = 'transparent'; addProfileButton.style.color = '#fff'; addProfileButton.style.borderRadius = '20px'; addProfileButton.style.cursor = 'pointer'; addProfileButton.onmouseover = () => { addProfileButton.style.backgroundColor = '#424242'; }; addProfileButton.onmouseout = () => { addProfileButton.style.backgroundColor = 'transparent'; }; addProfileButton.onclick = function() { openAddProfileDialog(); }; // Create key-value input container const keyValueContainer = document.createElement('div'); keyValueContainer.style.flex = '2.3'; keyValueContainer.style.display = 'flex'; keyValueContainer.style.flexDirection = 'column'; keyValueContainer.style.gap = '10px'; // Create entries list const entriesList = document.createElement('div'); entriesList.style.overflowY = 'auto'; entriesList.style.height = '340px'; entriesList.style.width = '100%'; entriesList.style.paddingRight = '20px'; entriesList.style.borderCollapse = 'collapse'; entriesList.style.display = 'block'; entriesList.style.overflowY = 'auto'; // Create a header above the headerRow const manageEventsHeader = document.createElement('h2'); manageEventsHeader.innerText = 'Manage Event List'; manageEventsHeader.style.marginBottom = '-10px'; closeButton.appendChild(closeIcon); eventsProfilePanel.appendChild(closeButton); profileListContainer.appendChild(profileListHeader); profileListContainer.appendChild(profileList); profileListContainer.appendChild(addProfileButton); keyValueContainer.appendChild(manageEventsHeader); // Create table header const headerRow = document.createElement('tr'); const headers = ['Key', 'Value', '%', 'Time', '']; headers.forEach(headerText => { const header = document.createElement('th'); header.innerText = headerText; header.style.padding = '5px'; header.style.textAlign = 'left'; if (headerText === 'Value') { header.style.width = '60%'; } else if (headerText === 'Key') { header.style.width = '15%'; } else if (headerText === '%' || headerText === 'Time') { header.style.width = '10%'; } else if (headerText === '') { header.style.width = '5%'; } headerRow.appendChild(header); }); const headerContainer = document.createElement('div'); headerContainer.style.position = 'sticky'; headerContainer.style.top = '0'; headerContainer.style.backgroundColor = '#2F2F2F'; headerContainer.style.zIndex = '1'; headerContainer.appendChild(headerRow); entriesList.appendChild(headerContainer); entriesList.style.position = 'sticky'; entriesList.style.top = '0'; entriesList.style.backgroundColor = '#2F2F2F'; // Create a separate header for the entries list const entriesHeaderContainer = document.createElement('div'); entriesHeaderContainer.style.position = 'sticky'; entriesHeaderContainer.style.top = '0'; entriesHeaderContainer.style.backgroundColor = '#2F2F2F'; entriesHeaderContainer.style.zIndex = '1'; const entriesHeader = document.createElement('div'); entriesHeader.style.display = 'flex'; entriesHeader.style.padding = '5px 0'; entriesHeader.style.borderBottom = '1px solid #444444'; headers.forEach(headerText => { const header = document.createElement('div'); header.innerText = headerText; header.style.padding = '5px'; header.style.textAlign = 'left'; // Align headers to the left if (headerText === 'Value') { header.style.width = '57%'; } else if (headerText === 'Key') { header.style.width = '15%'; } else if (headerText === '%' || headerText === 'Time') { header.style.width = '7%'; } else if (headerText === '') { header.style.width = '5%'; } entriesHeader.appendChild(header); }); entriesHeaderContainer.appendChild(entriesHeader); keyValueContainer.appendChild(entriesHeaderContainer); keyValueContainer.appendChild(entriesList); // Create container for probability and time range inputs const probTimeContainer = document.createElement('div'); probTimeContainer.style.display = 'flex'; probTimeContainer.style.gap = '10px'; probTimeContainer.style.marginBottom = '-4px'; // Create probability label and input const probabilityContainer = document.createElement('div'); probabilityContainer.style.display = 'flex'; probabilityContainer.style.flexDirection = 'column'; probabilityContainer.style.width = '30%'; // Adjust width to fit the row better const probabilityLabel = document.createElement('div'); probabilityLabel.innerText = 'Event Probability'; probabilityLabel.style.color = 'white'; probabilityLabel.style.marginBottom = '0px'; probabilityContainer.appendChild(probabilityLabel); const probabilityInputContainer = document.createElement('div'); probabilityInputContainer.style.display = 'flex'; probabilityInputContainer.style.alignItems = 'center'; const probabilityInput = document.createElement('input'); probabilityInput.type = 'number'; probabilityInput.placeholder = '0-100'; probabilityInput.style.backgroundColor = '#1E1E1E'; probabilityInput.style.color = 'white'; probabilityInput.style.border = '1px solid #444444'; probabilityInput.style.borderRadius = '5px'; probabilityInput.style.padding = '5px'; probabilityInput.style.width = '85%'; probabilityInput.style.marginRight = '5px'; probabilityInputContainer.appendChild(probabilityInput); const probabilityPercentLabel = document.createElement('span'); probabilityPercentLabel.innerText = '%'; probabilityPercentLabel.style.color = 'white'; probabilityInputContainer.appendChild(probabilityPercentLabel); probabilityContainer.appendChild(probabilityInputContainer); probTimeContainer.appendChild(probabilityContainer); // Create time range label and input const timeRangeContainer = document.createElement('div'); timeRangeContainer.style.display = 'flex'; timeRangeContainer.style.flexDirection = 'column'; timeRangeContainer.style.width = '30%'; // Adjust width to fit the row better const timeRangeLabel = document.createElement('div'); timeRangeLabel.innerText = 'Time Range'; timeRangeLabel.style.color = 'white'; timeRangeLabel.style.marginBottom = '0px'; timeRangeContainer.appendChild(timeRangeLabel); const timeRangeInputContainer = document.createElement('div'); timeRangeInputContainer.style.display = 'flex'; timeRangeInputContainer.style.alignItems = 'center'; const timeRangeInput = document.createElement('input'); timeRangeInput.type = 'text'; timeRangeInput.placeholder = '0-24'; timeRangeInput.value = '0-24'; // Set default value timeRangeInput.style.backgroundColor = '#1E1E1E'; timeRangeInput.style.color = 'white'; timeRangeInput.style.border = '1px solid #444444'; timeRangeInput.style.borderRadius = '5px'; timeRangeInput.style.padding = '5px'; timeRangeInput.style.width = '85%'; timeRangeInput.style.marginRight = '5px'; timeRangeInput.addEventListener('blur', () => { const timeValue = timeRangeInput.value.trim(); const timeRegex = /^([0-9]|1[0-9]|2[0-3])-(?:[0-9]|1[0-9]|2[0-3])$/; if (!timeRegex.test(timeValue)) { alert('Please enter a valid time range between 0-23, e.g., "8-16" or "0-24". Defaulting to "0-24".'); timeRangeInput.value = '0-24'; } }); timeRangeInputContainer.appendChild(timeRangeInput); const timeRangeUnitLabel = document.createElement('span'); timeRangeUnitLabel.innerText = 'h'; timeRangeUnitLabel.style.color = 'white'; timeRangeInputContainer.appendChild(timeRangeUnitLabel); timeRangeContainer.appendChild(timeRangeInputContainer); probTimeContainer.appendChild(timeRangeContainer); // Create overall probability label and input const overallProbabilityContainer = document.createElement('div'); overallProbabilityContainer.style.display = 'flex'; overallProbabilityContainer.style.flexDirection = 'column'; overallProbabilityContainer.style.width = '30%'; // Adjust width to fit the row better const overallProbabilityLabel = document.createElement('div'); overallProbabilityLabel.innerText = 'Overall Probability'; overallProbabilityLabel.style.color = 'white'; overallProbabilityLabel.style.marginBottom = '0px'; overallProbabilityContainer.appendChild(overallProbabilityLabel); const overallProbabilityInputContainer = document.createElement('div'); overallProbabilityInputContainer.style.display = 'flex'; overallProbabilityInputContainer.style.alignItems = 'center'; const overallProbabilityInput = document.createElement('input'); overallProbabilityInput.type = 'number'; overallProbabilityInput.placeholder = '0-100'; overallProbabilityInput.style.backgroundColor = '#202530'; overallProbabilityInput.style.color = 'white'; overallProbabilityInput.style.border = '1px solid #444444'; overallProbabilityInput.style.borderRadius = '5px'; overallProbabilityInput.style.padding = '5px'; overallProbabilityInput.style.marginRight = '5px'; overallProbabilityInput.style.width = '85%'; // Load the existing overall probability value if set const savedProbability = localStorage.getItem('events.probability'); if (savedProbability) { overallProbabilityInput.value = savedProbability; } // Add event listener to save the value to localStorage when changed overallProbabilityInput.addEventListener('input', () => { const probabilityValue = overallProbabilityInput.value.trim(); if (probabilityValue !== '' && probabilityValue >= 0 && probabilityValue <= 100) { localStorage.setItem('events.probability', probabilityValue); } else { alert('Please enter a valid probability between 0 and 100.'); } }); overallProbabilityInputContainer.appendChild(overallProbabilityInput); const overallProbabilityPercentLabel = document.createElement('span'); overallProbabilityPercentLabel.innerText = '%'; overallProbabilityPercentLabel.style.color = 'white'; overallProbabilityInputContainer.appendChild(overallProbabilityPercentLabel); overallProbabilityContainer.appendChild(overallProbabilityInputContainer); probTimeContainer.appendChild(overallProbabilityContainer); // Append probability and time range container to the main keyValue container keyValueContainer.appendChild(probTimeContainer); // Create input for key const keyInput = document.createElement('input'); keyInput.type = 'text'; keyInput.placeholder = 'Enter key'; keyInput.style.backgroundColor = '#1E1E1E'; keyInput.style.color = 'white'; keyInput.style.border = '1px solid #444444'; keyInput.style.borderRadius = '5px'; keyInput.style.padding = '5px'; keyInput.style.marginBottom = '-4px'; keyValueContainer.appendChild(keyInput); // Create input for value const valueInput = document.createElement('textarea'); valueInput.placeholder = 'Enter value'; valueInput.style.backgroundColor = '#1E1E1E'; valueInput.style.color = 'white'; valueInput.style.border = '1px solid #444444'; valueInput.style.borderRadius = '5px'; valueInput.style.padding = '5px'; valueInput.style.height = '80px'; valueInput.style.overflowWrap = 'break-word'; valueInput.style.overflow = 'auto'; valueInput.style.marginBottom = '-4px'; keyValueContainer.appendChild(valueInput); // Create button to add key-value pair const addEntryButton = document.createElement('button'); addEntryButton.innerText = 'Add Entry'; addEntryButton.innerText = 'Add Entry'; addEntryButton.style.padding = '10px 20px'; addEntryButton.style.border = '0.2px solid #4E4E4E'; addEntryButton.style.backgroundColor = '#2F2F2F'; addEntryButton.style.color = '#fff'; addEntryButton.style.borderRadius = '50px'; addEntryButton.style.cursor = 'pointer'; addEntryButton.onmouseover = () => { addEntryButton.style.backgroundColor = '#424242'; }; addEntryButton.onmouseout = () => { addEntryButton.style.backgroundColor = 'transparent'; }; // Adding Entries let currentEditingKey = null; // This keeps track of the current key being edited // Add or Edit Entry Button addEntryButton.onclick = function () { if (!selectedProfile) { alert('Please select a profile before adding entries.'); return; } const key = keyInput.value.trim(); const value = `<Event: ${valueInput.value.trim()}>`; const probability = probabilityInput.value.trim(); const timeRange = timeRangeInput.value.trim(); const fullKey = `${selectedProfile}.events:${key}`; if (key && value) { // Check if we are editing an existing entry if (currentEditingKey) { // Check if we are attempting to edit to a key that already exists if (currentEditingKey !== fullKey && localStorage.getItem(fullKey)) { alert('An entry with this key already exists. Please use a different key.'); return; } // Remove the old entry if the key has changed if (currentEditingKey !== fullKey) { localStorage.removeItem(currentEditingKey); } // Update or add the new entry const entryData = { value: value, probability: probability || '100', timeRange: timeRange || '0-24' }; localStorage.setItem(fullKey, JSON.stringify(entryData)); currentEditingKey = null; // Reset the editing key } else { // If adding a new entry if (!localStorage.getItem(fullKey)) { const entryData = { value: value, probability: probability || '100', timeRange: timeRange || '0-24' }; localStorage.setItem(fullKey, JSON.stringify(entryData)); } else { alert('Entry with this key already exists. Please use a different key.'); return; } } loadEntries(); // Clear the input fields after adding/editing the entry keyInput.value = ''; valueInput.value = ''; probabilityInput.value = '100'; timeRangeInput.value = '0-24'; } }; keyValueContainer.appendChild(addEntryButton); // Append containers to profilePanel eventsProfilePanel.appendChild(profileListContainer); eventsProfilePanel.appendChild(keyValueContainer); // Load saved profiles and entries let selectedProfile = localStorage.getItem('selectedProfile.events'); function loadProfiles() { profileList.innerHTML = ''; for (let key in localStorage) { if (key.startsWith('events.profile:')) { const profileName = key.replace('events.profile:', ''); const listItem = document.createElement('li'); listItem.style.display = 'flex'; listItem.style.alignItems = 'center'; listItem.style.cursor = 'pointer'; const nameSpan = document.createElement('span'); nameSpan.innerText = profileName; nameSpan.style.flex = '1'; listItem.appendChild(nameSpan); listItem.onclick = function() { if (selectedProfile === profileName) { selectedProfile = null; localStorage.setItem('selectedProfile.events', ''); listItem.style.backgroundColor = ''; loadEntries(); } else { selectedProfile = profileName; localStorage.setItem('selectedProfile.events', selectedProfile); loadEntries(); highlightSelectedProfile(listItem); } }; const deleteButton = document.createElement('button'); // deleteButton.innerText = 'x'; deleteButton.style.backgroundColor = 'transparent'; deleteButton.style.borderRadius = '50%'; deleteButton.style.marginLeft = '10px'; deleteButton.style.border = 'none'; deleteButton.style.color = '#ffffff'; deleteButton.style.cursor = 'pointer'; deleteButton.style.padding = '14px'; deleteButton.style.width = '15px'; deleteButton.style.height = '15px'; deleteButton.style.display = 'flex'; deleteButton.style.alignItems = 'center'; deleteButton.style.justifyContent = 'center'; deleteButton.style.transition = 'background-color 0.1s'; // Hover effect deleteButton.addEventListener('mouseenter', () => { // closeButton.style.transform = 'scale(1.1)'; deleteButton.style.backgroundColor = '#676767'; }); deleteButton.addEventListener('mouseleave', () => { // closeButton.style.transform = 'scale(1)'; deleteButton.style.backgroundColor = 'transparent'; }); const closeIcon2 = document.createElement('span'); closeIcon2.innerText = '✕'; closeIcon2.style.fontSize = '16px'; closeIcon2.style.color = '#ffffff'; closeIcon2.style.position = 'relative'; closeIcon2.style.top = '-1px'; // Adjust this value to move the character up deleteButton.appendChild(closeIcon2); deleteButton.onclick = function(event) { event.stopPropagation(); localStorage.removeItem(`events.profile:${profileName}`); if (selectedProfile === profileName) { selectedProfile = null; localStorage.setItem('selectedProfile.events', ''); } loadProfiles(); loadEntries(); }; listItem.appendChild(deleteButton); if (selectedProfile === profileName) { highlightSelectedProfile(listItem); } profileList.appendChild(listItem); } } } function openAddProfileDialog() { const dialog = document.createElement('div'); dialog.style.position = 'fixed'; dialog.style.top = '50%'; dialog.style.left = '50%'; dialog.style.transform = 'translate(-50%, -50%)'; dialog.style.backgroundColor = '#424242'; dialog.style.padding = '20px'; dialog.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)'; dialog.style.zIndex = '1100'; const input = document.createElement('input'); input.type = 'text'; input.placeholder = 'Enter profile name'; dialog.appendChild(input); const addButton = document.createElement('button'); addButton.innerText = 'Add'; addButton.style.marginLeft = '10px'; addButton.onclick = function() { const profileName = input.value.trim(); if (profileName) { // Perform a case-insensitive check for existing profiles const existingProfiles = Object.keys(localStorage).filter(key => key.startsWith('events.profile:')); const profileExists = existingProfiles.some(key => key.toLowerCase() === `events.profile:${profileName.toLowerCase()}`); if (!profileExists) { localStorage.setItem(`events.profile:${profileName}`, JSON.stringify({})); loadProfiles(); document.body.removeChild(dialog); } else { alert('Profile name already exists. Please choose a different name.'); } } }; dialog.appendChild(addButton); const cancelButton = document.createElement('button'); cancelButton.innerText = 'Cancel'; cancelButton.style.marginLeft = '10px'; cancelButton.onclick = function() { document.body.removeChild(dialog); }; dialog.appendChild(cancelButton); document.body.appendChild(dialog); } function loadEntries() { entriesList.innerHTML = ''; // Add header back after clearing if (selectedProfile) { const prefix = `${selectedProfile}.events:`.toLowerCase(); for (let key in localStorage) { if (key.toLowerCase().startsWith(prefix)) { const entryData = JSON.parse(localStorage.getItem(key)); const row = document.createElement('div'); row.style.padding = '10px'; row.style.margin = '5px 0'; row.style.borderRadius = '10px'; row.style.marginBottom = '12px'; row.style.backgroundColor = '#424242'; row.style.display = 'flex'; row.style.alignItems = 'center'; // Create cells for key, value, probability, and time range const keyCell = document.createElement('div'); keyCell.innerText = key.split(':')[1]; keyCell.style.padding = '5px'; keyCell.style.width = '15%'; row.appendChild(keyCell); const valueCell = document.createElement('div'); valueCell.innerText = entryData.value.replace(/^<Event:\s*/, '').slice(0, -1); // Remove surrounding brackets valueCell.style.padding = '5px'; valueCell.style.width = '60%'; row.appendChild(valueCell); const probabilityCell = document.createElement('div'); probabilityCell.innerText = `${entryData.probability}%`; probabilityCell.style.padding = '5px'; probabilityCell.style.width = '10%'; row.appendChild(probabilityCell); const timeRangeCell = document.createElement('div'); timeRangeCell.innerText = entryData.timeRange; timeRangeCell.style.padding = '5px'; timeRangeCell.style.width = '10%'; row.appendChild(timeRangeCell); // Add remove button for each entry const actionCell = document.createElement('div'); actionCell.style.padding = '5px'; actionCell.style.width = '5%'; const removeButton = document.createElement('button'); // removeButton.innerText = '✕'; removeButton.style.backgroundColor = 'transparent'; removeButton.style.border = 'none'; removeButton.style.cursor = 'pointer'; removeButton.style.display = 'flex'; removeButton.style.alignItems = 'center'; removeButton.style.justifyContent = 'center'; removeButton.style.width = '100%'; removeButton.style.height = '100%'; removeButton.style.transition = 'background-color 0.2s ease'; removeButton.style.borderRadius = '50%'; removeButton.style.width = '28px'; removeButton.style.height = '28px'; removeButton.style.marginLeft = '-3px'; removeButton.style.boxSizing = 'border-box'; removeButton.onclick = function() { localStorage.removeItem(key); loadEntries(); }; const closeIcon1 = document.createElement('span'); closeIcon1.innerText = '✕'; closeIcon1.style.fontSize = '16px'; closeIcon1.style.position = 'relative'; closeIcon1.style.color = '#ffffff'; closeIcon1.style.top = '-1px'; // Adjust this value to move the character up // Append the span to the button removeButton.appendChild(closeIcon1); actionCell.appendChild(removeButton); row.appendChild(actionCell); // Hover effect removeButton.addEventListener('mouseenter', () => { removeButton.style.backgroundColor = '#676767'; }); removeButton.addEventListener('mouseleave', () => { removeButton.style.backgroundColor = 'transparent'; }); // Make the row editable when clicked // Make the row editable when clicked row.onclick = function() { keyInput.value = key.split(':')[1]; // If key processing remains as is valueInput.value = entryData.value.replace(/^<Event:\s*/, '').slice(0, -1); // Remove <Event: > prefix and surrounding brackets probabilityInput.value = entryData.probability; timeRangeInput.value = entryData.timeRange; currentEditingKey = key; // Set the current key for editing }; entriesList.appendChild(row); } } } } function highlightSelectedProfile(selectedItem) { // Remove highlight from all items Array.from(profileList.children).forEach(item => { item.style.backgroundColor = ''; }); // Highlight the selected item selectedItem.style.backgroundColor = '#444444'; selectedItem.style.borderRadius = '10px'; selectedItem.style.padding = '10px'; } loadProfiles(); loadEntries(); // Append profilePanel to body document.body.appendChild(eventsProfilePanel); } } // ==================================================================== RULES =================================================================== function RulesScript() { // Custom texts to cycle through let customRules = []; let currentIndex = 0; // Function to determine the current rule index by scanning last two messages function determineCurrentIndex() { const messageItems = document.querySelectorAll('li[class^="messageListItem_"]'); if (messageItems.length >= 1) { // Check the last message first const lastMessage = Array.from(messageItems[messageItems.length - 1].querySelectorAll('span')).map(span => span.innerText).join('') || messageItems[messageItems.length - 1].innerText; for (let i = 0; i < customRules.length; i++) { if (lastMessage.includes(`<Rule${i + 1}:`)) { currentIndex = (i + 1) % customRules.length; return; } } } // If not found in the last message, check the second to last message if (messageItems.length >= 2) { const secondLastMessage = Array.from(messageItems[messageItems.length - 2].querySelectorAll('span')).map(span => span.innerText).join('') || messageItems[messageItems.length - 2].innerText; for (let i = 0; i < customRules.length; i++) { if (secondLastMessage.includes(`<Rule${i + 1}:`)) { currentIndex = (i + 1) % customRules.length; return; } } } } // Expose necessary elements to be used by the second script window.customRuleLogic = { customRules, determineCurrentIndex, getCurrentText: function() { determineCurrentIndex(); const customRule = '\n' + customRules[currentIndex]; currentIndex = (currentIndex + 1) % customRules.length; return customRule; } }; // Create Button and Panel UI for Local Storage Key Management window.manageRulesButton = document.createElement('div'); window.manageRulesButton.innerHTML = ` <button id="toggle-rules-panel" style=" position: relative; top: 10px; right: 0px; left: 10px; padding: 7px 15px; background: transparent; color: #b0b0b0; border: none; border-radius: 8px; font-size: 16px; text-align: center; cursor: pointer; width: 90%; transition: background-color 0.1s, color 0.1s; z-index: 1001;"> Manage Rules </button> `; const rulesButtonElement = manageRulesButton.querySelector('button'); rulesButtonElement.onmouseover = () => { rulesButtonElement.style.backgroundColor = '#212121'; rulesButtonElement.style.color = '#ffffff'; }; rulesButtonElement.onmouseout = () => { rulesButtonElement.style.backgroundColor = 'transparent'; rulesButtonElement.style.color = '#b0b0b0'; }; const rulesPanel = document.createElement('div'); rulesPanel.style.position = 'fixed'; rulesPanel.style.top = '50%'; rulesPanel.style.left = '50%'; rulesPanel.style.transform = 'translate(-50%, -50%)'; // Size different for Mobile and Desktop if (window.innerWidth <= 768) { rulesPanel.style.width = '90%'; rulesPanel.style.height = '90%'; } else { rulesPanel.style.width = '800px'; rulesPanel.style.height = '700px'; } rulesPanel.style.backgroundColor = '#2f2f2f'; rulesPanel.style.padding = '20px'; rulesPanel.style.overflowY = 'auto'; rulesPanel.style.display = 'none'; rulesPanel.style.zIndex = 1000; rulesPanel.style.borderRadius = '10px'; document.body.appendChild(rulesPanel); const title = document.createElement('h2'); title.innerText = 'Manage Rules'; title.style.textAlign = 'center'; title.style.color = '#ffffff'; title.style.fontSize = '24px'; title.style.fontWeight = '550'; title.style.marginBottom = '20px'; rulesPanel.appendChild(title); const closeButton = document.createElement('button'); closeButton.innerText = '✕'; closeButton.style.position = 'absolute'; closeButton.style.borderRadius = '50%'; closeButton.style.color = '#ffffff'; closeButton.style.top = '20px'; closeButton.style.right = '20px'; closeButton.style.backgroundColor = 'transparent'; closeButton.style.cursor = 'pointer'; closeButton.addEventListener('mouseenter', () => { closeButton.style.backgroundColor = '#676767'; }); closeButton.addEventListener('mouseleave', () => { closeButton.style.backgroundColor = 'transparent'; }); closeButton.addEventListener('click', () => { rulesPanel.style.display = 'none'; }); rulesPanel.appendChild(closeButton); manageRulesButton.addEventListener('click', () => { rulesPanel.style.display = rulesPanel.style.display === 'none' ? 'block' : 'none'; renderPanel(); }); function renderPanel() { rulesPanel.innerHTML = ''; rulesPanel.appendChild(title); rulesPanel.appendChild(closeButton); customRules.forEach((rule, index) => { const ruleContainer = document.createElement('div'); ruleContainer.style.marginBottom = '15px'; ruleContainer.style.display = 'flex'; ruleContainer.style.alignItems = 'center'; ruleContainer.style.width = '95%'; ruleContainer.style.gap = '5px'; // Reduced gap to bring elements closer const ruleLabel = document.createElement('label'); ruleLabel.textContent = `Rule ${index + 1}:`; ruleLabel.style.color = 'white'; ruleLabel.style.marginLeft = '5%'; ruleLabel.style.flex = '0.5'; // Reduced flex to bring the label closer to the input ruleContainer.appendChild(ruleLabel); const ruleInput = document.createElement('input'); ruleInput.type = 'text'; ruleInput.placeholder = rule.replace(/<Rule\d+: (.*?)>/, '$1'); // Only display the inner text as a placeholder ruleInput.style.flex = '2'; ruleInput.style.padding = '5px'; ruleInput.style.borderRadius = '5px'; ruleInput.style.border = '0.5px solid #ccc'; ruleInput.style.backgroundColor = '#2F2F2F'; ruleInput.style.color = 'gray'; // Default color to indicate unsaved state ruleContainer.appendChild(ruleInput); // Change color to indicate unsaved changes ruleInput.addEventListener('input', () => { ruleInput.style.color = '#D16262'; }); const updateButton = document.createElement('button'); updateButton.textContent = 'Update'; updateButton.style.padding = '5px 10px'; updateButton.style.border = 'none'; updateButton.style.backgroundColor = '#1E1E1E'; updateButton.style.color = 'white'; updateButton.style.borderRadius = '5px'; updateButton.style.cursor = 'pointer'; updateButton.addEventListener('click', () => { customRules[index] = `<Rule${index + 1}: ${ruleInput.value}>`; updateLocalStorageKeys(); ruleInput.style.color = 'black'; // Change color to indicate saved state }); ruleContainer.appendChild(updateButton); const deleteButton = document.createElement('button'); deleteButton.textContent = 'Delete'; deleteButton.style.padding = '5px 10px'; deleteButton.style.border = 'none'; deleteButton.style.backgroundColor = '#1E1E1E'; deleteButton.style.color = 'white'; deleteButton.style.marginLeft = '5px'; deleteButton.style.borderRadius = '5px'; deleteButton.style.cursor = 'pointer'; deleteButton.addEventListener('click', () => { customRules.splice(index, 1); updateLocalStorageKeys(); renderPanel(); }); ruleContainer.appendChild(deleteButton); // Move rule up/down container const moveContainer = document.createElement('div'); moveContainer.style.display = 'flex'; moveContainer.style.flexDirection = 'column'; moveContainer.style.gap = '3px'; // Move rule up if (index > 0) { const upButton = document.createElement('button'); upButton.textContent = '▲'; upButton.style.padding = '3px'; upButton.style.border = 'none'; upButton.style.backgroundColor = 'transparent'; upButton.style.color = 'white'; upButton.style.borderRadius = '5px'; upButton.style.cursor = 'pointer'; upButton.addEventListener('click', () => { [customRules[index - 1], customRules[index]] = [customRules[index], customRules[index - 1]]; customRules = customRules.map((rule, i) => `<Rule${i + 1}: ${rule.match(/<Rule\d+: (.*?)>/)[1]}>`); updateLocalStorageKeys(); renderPanel(); }); moveContainer.appendChild(upButton); } // Move rule down if (index < customRules.length - 1) { const downButton = document.createElement('button'); downButton.textContent = '▼'; downButton.style.padding = '3px'; downButton.style.border = 'none'; downButton.style.backgroundColor = 'transparent'; downButton.style.color = 'white'; downButton.style.borderRadius = '5px'; downButton.style.cursor = 'pointer'; downButton.addEventListener('click', () => { [customRules[index], customRules[index + 1]] = [customRules[index + 1], customRules[index]]; customRules = customRules.map((rule, i) => `<Rule${i + 1}: ${rule.match(/<Rule\d+: (.*?)>/)[1]}>`); updateLocalStorageKeys(); renderPanel(); }); moveContainer.appendChild(downButton); } ruleContainer.appendChild(moveContainer); rulesPanel.appendChild(ruleContainer); }); const addButton = document.createElement('button'); addButton.textContent = 'Add Rule'; addButton.style.margin = '15px auto'; addButton.style.display = 'block'; addButton.style.padding = '10px'; addButton.style.border = 'none'; addButton.style.width = '90%'; addButton.style.backgroundColor = '#e0e0e0'; addButton.style.borderRadius = '20px'; addButton.style.cursor = 'pointer'; addButton.addEventListener('click', () => { const newRule = `<Rule${customRules.length + 1}: Define your rule here>`; customRules.push(newRule); updateLocalStorageKeys(); renderPanel(); }); rulesPanel.appendChild(addButton); } function updateLocalStorageKeys() { customRules.forEach((_, index) => localStorage.removeItem(`Rule${index + 1}`)); customRules.forEach((rule, index) => { localStorage.setItem(`Rule${index + 1}`, rule); }); } // Load rules from localStorage for (let i = 0; i < localStorage.length; i++) { const savedRule = localStorage.getItem(`Rule${i + 1}`); if (savedRule) { customRules.push(savedRule); } } } // ===================================================================== MP3 ==================================================================== function mp3Script() { // Function to get API key from local storage or prompt user to input it function getApiKey() { let apiKey = localStorage.getItem('google_cloud_api_key'); return apiKey; } // Function to set the API key in local storage document.addEventListener('input', (event) => { if (event.target.id === 'api-key-input') { const apiKey = event.target.value; if (apiKey) { localStorage.setItem('google_cloud_api_key', apiKey); alert('API key saved successfully.'); } } }); // Fetch the API key let API_KEY = getApiKey(); const API_URL = `https://speech.googleapis.com/v1/speech:recognize?key=${API_KEY}`; // Create a button to toggle the transcription panel window.mp3ToggleButton = document.createElement('div'); mp3ToggleButton.innerHTML = ` <button id="toggle-transcription-panel" style=" position: relative; top: 10px; right: 0px; left: 10px; padding: 7px 15px; margin-top: 20px; background: transparent; color: #b0b0b0; border: none; border-radius: 8px; font-size: 16px; text-align: center; cursor: pointer; width: 90%; transition: background-color 0.1s, color 0.1s; z-index: 1001;"> Show MP3 Transcription </button> `; const buttonElement = mp3ToggleButton.querySelector('button'); buttonElement.onmouseover = () => { buttonElement.style.backgroundColor = '#212121'; buttonElement.style.color = '#ffffff'; }; buttonElement.onmouseout = () => { buttonElement.style.backgroundColor = 'transparent'; buttonElement.style.color = '#b0b0b0'; }; // Append the button to the body document.body.appendChild(mp3ToggleButton); // Add a simple panel to the webpage const panelHTML = ` <div id="transcription-panel" style="display: none;"> <h3>MP3 Transcription Tool</h3> <input type="text" id="api-key-input" placeholder="Enter Google Cloud API key here" /> <input type="text" id="mp3-url" placeholder="Enter MP3 URL here" /> <button id="transcribe-button">Transcribe</button> <textarea id="transcription-result" placeholder="Transcript will appear here..." readonly></textarea> </div> `; document.body.insertAdjacentHTML('beforeend', panelHTML); // Set the API key input value if it exists in local storage document.addEventListener('DOMContentLoaded', () => { const apiKeyInput = document.getElementById('api-key-input'); if (apiKeyInput) { const savedApiKey = getApiKey(); if (savedApiKey) { apiKeyInput.value = savedApiKey; API_KEY = savedApiKey; } } }); // Add styles for the panel GM_addStyle(` #transcription-panel { position: fixed; bottom: 50px; right: 10px; width: 300px; background: #f8f9fa; border: 1px solid #ccc; padding: 10px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); z-index: 9999; font-family: Arial, sans-serif; } #transcription-panel h3 { margin: 0 0 10px; font-size: 16px; } #transcription-panel input, #transcription-panel textarea { width: 100%; margin-bottom: 10px; padding: 5px; box-sizing: border-box; } #transcription-panel button { width: 100%; padding: 5px; cursor: pointer; background: #007bff; color: white; border: none; border-radius: 3px; } `); // Add event listener to the toggle button document.addEventListener('click', (event) => { // Check if the clicked element is the button const button = event.target.closest('#toggle-transcription-panel'); if (button) { const panel = document.getElementById('transcription-panel'); if (panel) { if (panel.style.display === 'none') { panel.style.display = 'block'; button.innerText = 'Hide MP3 Transcription'; } else { panel.style.display = 'none'; button.innerText = 'Show MP3 Transcription'; } } else { console.error('Transcription panel not found!'); } } }); // Add event listener to the Transcribe button document.getElementById('transcribe-button').addEventListener('click', transcribe); // Function to transcribe the entered MP3 URL function transcribe() { const mp3Url = document.getElementById('mp3-url').value.trim(); if (!mp3Url) { alert('Please enter a valid MP3 URL.'); return; } // Check if the transcript already exists in local storage const storedTranscript = localStorage.getItem(mp3Url); if (storedTranscript) { document.getElementById('transcription-result').value = storedTranscript; return; } // Fetch MP3 file using GM_xmlhttpRequest and process it GM_xmlhttpRequest({ method: 'GET', url: mp3Url, responseType: 'arraybuffer', // Required for audio data onload: (response) => { // Convert the audio file to Base64 const audioBase64 = arrayBufferToBase64(response.response); // Send the Base64-encoded audio to Google Cloud Speech-to-Text API sendToGoogleCloud(audioBase64, mp3Url); }, onerror: (err) => { alert('Failed to fetch the MP3 file.'); console.error(err); }, }); } // Function to send the Base64 audio data to Google Cloud Speech-to-Text API function sendToGoogleCloud(audioBase64, mp3Url) { GM_xmlhttpRequest({ method: 'POST', url: API_URL, headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ config: { encoding: 'MP3', sampleRateHertz: 16000, languageCode: 'en-US', }, audio: { content: audioBase64, }, }), onload: (response) => { const result = JSON.parse(response.responseText); if (result.error) { alert(`Error: ${result.error.message}`); } else { const transcript = result.results ?.map((r) => r.alternatives[0].transcript) .join('\n'); document.getElementById('transcription-result').value = transcript || 'No transcript found.'; // Store the transcript in local storage localStorage.setItem(mp3Url, transcript || 'No transcript found.'); // Limit local storage to 3 entries manageLocalStorageLimit(); // Show "Transcript done!" message showTranscriptionDoneMessage(); } }, onerror: (err) => { alert('Failed to process the transcription.'); console.error(err); }, }); } // Function to convert ArrayBuffer to Base64 function arrayBufferToBase64(buffer) { const binary = []; const bytes = new Uint8Array(buffer); const len = bytes.byteLength; for (let i = 0; i < len; i++) { binary.push(String.fromCharCode(bytes[i])); } return btoa(binary.join('')); } // Observe the conversation for changes and detect MP3 links const observer = new MutationObserver(() => { const messageItems = document.querySelectorAll('div[class*="messageContent_"]'); const lastMessage = messageItems[messageItems.length - 1]; if (lastMessage) { const mp3LinkMatch = lastMessage.innerText.match(/https:\/\/files\.shapes\.inc\/.*\.mp3/); if (mp3LinkMatch) { const mp3Link = mp3LinkMatch[0]; const storedLink = localStorage.getItem('lastMp3Link'); // Check if the link is new or different if (mp3Link !== storedLink) { localStorage.setItem('lastMp3Link', mp3Link); // Store the new link document.getElementById('mp3-url').value = mp3Link; // Populate the input transcribe(); // Automatically transcribe the new link } } } }); // Start observing the document body for new messages observer.observe(document.body, { childList: true, subtree: true }); // Function to manage local storage limit of 3 entries function manageLocalStorageLimit() { const keys = Object.keys(localStorage).filter((key) => key.startsWith('http')); if (keys.length > 10) { // Remove oldest entries until only 3 remain while (keys.length > 10) { localStorage.removeItem(keys.shift()); } } } // Function to show "Transcript done!" message function showTranscriptionDoneMessage() { let messageDiv = document.getElementById('transcription-done-message'); if (!messageDiv) { messageDiv = document.createElement('div'); messageDiv.id = 'transcription-done-message'; messageDiv.innerText = 'Transcript done!'; document.body.appendChild(messageDiv); } messageDiv.style.display = 'block'; setTimeout(() => { messageDiv.style.display = 'none'; }, 3000); } } // =================================================================== STYLES =================================================================== function StylesScript() { // Add custom CSS rule to modify the channel text area const style = document.createElement('style'); style.innerHTML = ` .channelTextArea_bdf0de { position: relative; width: 120%; text-indent: 0; border-radius: 8px; } .themedBackground_bdf0de { background: #2F2F2F; } .chatContent_a7d72e { position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 0; flex: 1 1 auto; background: #212121 !important; } .theme-dark .container_ee69e0 { background: #191919; } .theme-dark .themed_fc4f04 { background: #212121; } .footer_f8ec41 { background: #131313; } .theme-dark .container_b2ca13 { background: #191919; } .wrapper_fea3ef { background-color: #131313; display: none !important; } `; document.head.appendChild(style); // Function to hide the targeted elements function hideElements() { // Select all elements that match the provided pattern const elements = document.querySelectorAll("[id^='message-accessories-'] > article > div > div > div.embedProvider_b0068a.embedMargin_b0068a, [id^='message-accessories-'] > article > div > div > div.embedTitle_b0068a.embedMargin_b0068a, .buttons_bdf0de .expression-picker-chat-input-button.buttonContainer_bdf0de, .channelAppLauncher_df39bd .buttonContainer_df39bd.app-launcher-entrypoint"); // Iterate over each element and hide it elements.forEach(element => { element.style.display = 'none'; }); } // Run the function initially to hide elements present at page load hideElements(); // Observe mutations to the DOM and hide elements when new ones are added const observer = new MutationObserver(hideElements); observer.observe(document.body, { childList: true, subtree: true }); // Create the toggleButton div // Create the toggleButton div window.serverbartoggleButton = document.createElement('div'); serverbartoggleButton .innerHTML = ` <button id="toggle-server-panel" style=" position: relative; top: 10px; right: 0px; left: 10px; padding: 7px 15px; margin-top: 10px; background: transparent; color: #b0b0b0; border: none; border-radius: 8px; font-size: 16px; text-align: center; cursor: pointer; width: 90%; transition: background-color 0.1s, color 0.1s; z-index: 1001;"> Toggle Sidebar </button> `; // Append the toggleButton to the desired parent element (e.g., `DCstoragePanel`) //window.DCstoragePanel.appendChild(serverbartoggleButton); // Function to toggle .itemsContainer_fea3ef visibility function servertoggleItemsContainer() { const serveritemsContainer = document.querySelectorAll('.wrapper_fea3ef'); serveritemsContainer.forEach(container => { const currentDisplay = window.getComputedStyle(container).getPropertyValue('display'); if (currentDisplay === 'none') { container.setAttribute('style', 'display: flex !important;'); } else { container.setAttribute('style', 'display: none !important;'); } }); } // Add event listener to the toggle button serverbartoggleButton.addEventListener('click', servertoggleItemsContainer); const serverbuttonElement = serverbartoggleButton.querySelector('button'); serverbuttonElement.onmouseover = () => { serverbuttonElement.style.backgroundColor = '#212121'; serverbuttonElement.style.color = '#ffffff'; }; serverbuttonElement.onmouseout = () => { serverbuttonElement.style.backgroundColor = 'transparent'; serverbuttonElement.style.color = '#b0b0b0'; }; } // ================================================================== < > HIDER ================================================================= function HiderScript() { function hideEnclosedEntries() { const messageItems = document.querySelectorAll('li[class^="messageListItem_"]'); messageItems.forEach(messageItem => { const spans = messageItem.querySelectorAll('div[class*="messageContent_"] span'); let isHiding = false; spans.forEach(span => { const text = span.textContent.trim(); // Start hiding when encountering '<' if (text.startsWith('<') && !isHiding) { isHiding = true; } // Apply hiding style if within an enclosed entry if (isHiding) { span.style.opacity = '0'; // Make it invisible span.style.position = 'absolute'; // Remove it from the document flow } // Stop hiding when encountering '>' if (text.endsWith('>') && isHiding) { isHiding = false; } }); }); } // Observe for new messages being added to the DOM const observer = new MutationObserver(mutations => { mutations.forEach(() => { hideEnclosedEntries(); }); }); // Start observing the entire document body for changes observer.observe(document.body, { childList: true, subtree: true }); // Initial run hideEnclosedEntries(); } // ================================================================= MAIN LOGIC ================================================================= function MainLogicScript() { // 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 the settings window window.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); // 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)); }); // Function to get the correct input element based on the mode // Function to get the correct input element based on the mode // 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_"]'); } // Add event listener to handle Enter key behavior window.addEventListener('keydown', function(event) { const inputElement = getInputElement(); if (event.key === 'Enter' && !event.shiftKey && !enterKeyDisabled) { if (inputElement && inputElement.nodeName === 'TEXTAREA') { // Mobile version: Only allow line break return; } 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 // Add event listener to the send button to execute handleEnterKey when clicked window.addEventListener('click', function(event) { const sendButton = document.querySelector('button[aria-label="Nachricht senden"]'); if (sendButton && sendButton.contains(event.target)) { // Execute main handler for Enter key handleEnterKey(); } }, true); // Main function that handles Enter key behavior function handleEnterKey() { let inputElement = getInputElement(); if (inputElement) { inputElement.focus(); setCursorToEnd(inputElement); if (customRuleEnabled) { applyCustomRule(inputElement); } setCursorToEnd(inputElement); if (scanForKeywordsEnabled) { scanForKeywords(inputElement); } getRandomEntry(inputElement); sendMessage(inputElement); anotherCustomFunction(); } } // Function to apply custom rules for the input field function applyCustomRule(inputElement) { const customRule = window.customRuleLogic ? window.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') { // For mobile version where input is <textarea> inputElement.selectionStart = inputElement.selectionEnd = inputElement.value.length; } else { // For desktop version where input is a Slate editor inputElement.focus(); // Simulate repeated Ctrl + ArrowRight key press events to move cursor to the end const repeatPresses = 150; // Number of times to simulate Ctrl + ArrowRight for (let i = 0; i < repeatPresses; i++) { const ctrlArrowRightEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', code: 'ArrowRight', keyCode: 39, // ArrowRight key code charCode: 0, which: 39, bubbles: true, cancelable: true, ctrlKey: true // Set Ctrl key to true }); inputElement.dispatchEvent(ctrlArrowRightEvent); } } } // Function to send the message (either click send button or simulate Enter key) function sendMessage(inputElement) { if (inputElement.nodeName === 'TEXTAREA') { // Mobile version: Do not send message, just return to allow linebreak return; } 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 get the current profile from local storage function getCurrentProfile() { return localStorage.getItem('selectedProfile.lorebook'); } // 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_"]'); let relevantMessages = Array.from(messageItems).slice(-15); // Last 15 messages const lastMessage = Array.from(messageItems).slice(-1); // Last message only // Iterate over the last 15 messages to extract hidden bracket content relevantMessages = relevantMessages.map(msg => { // Retrieve all span elements within the message const spans = msg.querySelectorAll('span'); // Filter out the spans based on both style conditions: opacity and position const hiddenSpans = Array.from(spans).filter(span => { const style = window.getComputedStyle(span); return style.opacity === '0' && style.position === 'absolute'; }); // Join the text content of all matching spans, converting them to lowercase const bracketContent = hiddenSpans.map(span => span.textContent.toLowerCase()).join(''); // Extract content within square brackets, if any const match = bracketContent.match(/\[(.*?)\]/); return match ? match[1] : null; }).filter(content => content !== null); // Log the filtered messages for debugging purposes console.log("Filtered Relevant Messages (content in brackets, last 15):", relevantMessages); console.log("Last Message:", lastMessage.map(msg => msg.textContent)); // Track how many entries have been appended let appendedCount = 0; const maxAppends = 3; // Check if the last message contains a specific link pattern const mp3LinkPattern = /https:\/\/files\.shapes\.inc\/.*\.mp3/; let mp3LinkValue = null; if (lastMessage.length > 0) { const lastMessageText = lastMessage[0].textContent.toLowerCase(); const mp3LinkMatch = lastMessageText.match(mp3LinkPattern); if (mp3LinkMatch) { const mp3LinkKey = mp3LinkMatch[0]; mp3LinkValue = localStorage.getItem(mp3LinkKey); console.log(`MP3 Link detected: ${mp3LinkKey}. Retrieved value: ${mp3LinkValue}`); } } // Create an array to hold all entry keys that need to be checked let allEntryKeys = []; // 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 => { allEntryKeys.push({ entryKey, entryValue }); }); } }); // If mp3LinkValue is present, parse it for keywords as well if (mp3LinkValue) { console.log(`Scanning MP3 link value for keywords: ${mp3LinkValue}`); const mp3Keywords = mp3LinkValue.split(','); mp3Keywords.forEach(keyword => { const trimmedKeyword = keyword.trim(); console.log(`Adding keyword from MP3 value: ${trimmedKeyword}`); // Add mp3 keywords but set entryValue to an empty string instead of null allEntryKeys.push({ entryKey: trimmedKeyword, entryValue: '' }); }); } // Iterate over all collected entry keys and perform the checks allEntryKeys.forEach(({ entryKey, entryValue }) => { if (appendedCount >= maxAppends) return; // Stop if max appends reached // Log each keyword being checked console.log(`Checking keyword: ${entryKey}`); // Check input element text for complete word match of keyword (case-insensitive) const inputText = inputElement.value || inputElement.textContent; // Combine check for keyword in input, in the last message, or in the mp3 link value const isKeywordInInput = inputText && new RegExp(`\\b${entryKey}\\b`, 'i').test(inputText); const isKeywordInLastMessage = lastMessage.some(message => { const lastMessageText = message.textContent; return new RegExp(`\\b${entryKey}\\b`, 'i').test(lastMessageText); }); const isKeywordInMp3LinkValue = mp3LinkValue && mp3LinkValue.includes(entryKey); console.log(`Keyword '${entryKey}' in input: ${isKeywordInInput}, in last message: ${isKeywordInLastMessage}, in MP3 value: ${isKeywordInMp3LinkValue}`); if ((isKeywordInInput || isKeywordInLastMessage || isKeywordInMp3LinkValue) && entryValue) { const keywordAlreadyUsed = relevantMessages.some(bracketContent => { return new RegExp(`\\b${entryKey}\\b`, 'i').test(bracketContent); }); if (!keywordAlreadyUsed) { // Append the entryValue to the input element only if entryValue is not null or empty 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 or entryValue is null/empty. Skipping append.`); } } }); // Log the total number of entries appended console.log(`Total lorebook entries appended: ${appendedCount}`); } } function getRandomEntry(inputElement) { const selectedProfile = localStorage.getItem('selectedProfile.events'); if (selectedProfile) { let profileEntries = []; const currentHour = new Date().getHours(); for (let key in localStorage) { if (key.startsWith(`${selectedProfile}.events:`)) { const entryData = JSON.parse(localStorage.getItem(key)); const [startHour, endHour] = entryData.timeRange.split('-').map(Number); // Check if current hour is within the time range if ( (startHour <= endHour && currentHour >= startHour && currentHour < endHour) || // Normal range (startHour > endHour && (currentHour >= startHour || currentHour < endHour)) // Crosses midnight ) { 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 window.getRandomEntry = getRandomEntry; } } // =================================================================== BUTTONS ================================================================== function initializeButton() { DCstoragePanel.appendChild(openLorebookButton); DCstoragePanel.appendChild(eventsButton); DCstoragePanel.appendChild(manageRulesButton); DCstoragePanel.appendChild(serverbartoggleButton); DCstoragePanel.appendChild(mp3ToggleButton); DCstoragePanel.appendChild(exportButton); DCstoragePanel.appendChild(importButton); DCstoragePanel.appendChild(settingsWindow); } // ============================================================ SCRIPT LOADING ORDER ============================================================ LorebookScript(); ImportExportScript(); EventsScript(); RulesScript(); mp3Script(); StylesScript(); HiderScript(); MainLogicScript(); initializeButton(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址