您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Inserts a JannyAI button into Character and My Chats Pages for convenience.
// ==UserScript== // @name JanitorAI JannyAI Buttons // @namespace http://tampermonkey.net/ // @version 1.0 // @description Inserts a JannyAI button into Character and My Chats Pages for convenience. // @author firecat (and Selene) // @match https://janitorai.com/* // @grant none // @license WTFPL // ==/UserScript== (function() { 'use strict'; // Debug configuration const DEBUG_MODE = false; // Set to true for debugging let processing = false; const DEBOUNCE_DELAY = 300; const INITIAL_DELAY = 800; let retryCount = 0; const MAX_RETRIES = 2; const debugLog = (...args) => { if (DEBUG_MODE) { console.log('[JannyAI-Redirect]', ...args); } }; const shouldRunOnPage = () => { return window.location.pathname.includes('/characters/') || window.location.pathname === '/my_chats'; }; const createJannyButton = (originalUrl) => { debugLog('Creating JannyAI button for URL:', originalUrl); const button = document.createElement('button'); button.className = 'jannyai-button'; button.innerHTML = '<span style="margin-right:6px">🃏</span>JannyAI'; button.style.cssText = ` background: #20B2AA !important; color: white !important; margin: 8px 0 !important; padding: 8px 16px !important; border-radius: 4px !important; font-size: 0.9rem !important; font-family: Jura,sans-serif !important; height: var(--chakra-sizes-8); display: inline-flex; appearance: none; -moz-box-align: center; align-items: center; -moz-box-pack: center; justify-content: center; user-select: none; position: relative; white-space: nowrap; vertical-align: middle; outline: transparent solid 2px; outline-offset: 2px; line-height: 1.2; border-radius: var(--chakra-radii-md); font-weight: var(--chakra-fontWeights-semibold); transition-property: var(--chakra-transition-property-common); transition-duration: var(--chakra-transition-duration-normal); height: var(--chakra-sizes-8); min-width: var(--chakra-sizes-8); font-size: var(--chakra-fontSizes-sm); padding-inline-start: var(--chakra-space-3); padding-inline-end: var(--chakra-space-3); background: var(--chakra-colors-red-200); color: var(--chakra-colors-gray-800); `; button.onclick = () => { debugLog('Button clicked - redirecting to JannyAI'); window.open(originalUrl.replace('janitorai.com', 'jannyai.com'), '_blank'); }; return button; }; const handle404Page = () => { if (!shouldRunOnPage() || processing) { debugLog('Already processing or not target page - skipping 404 check'); return; } processing = true; debugLog('Processing 404 page check'); const notFoundContainer = document.querySelector('.css-64aicp')?.parentElement; if (!notFoundContainer) { debugLog('No 404 container found'); processing = false; return; } if (notFoundContainer.querySelector('.jannyai-button')) { debugLog('JannyAI button already exists on 404 page'); processing = false; return; } const homePageLink = notFoundContainer.querySelector('a[href="/"]'); if (!homePageLink) { debugLog('Home page link not found in 404 container'); processing = false; return; } debugLog('Injecting JannyAI button into 404 page'); const jannyButton = createJannyButton(window.location.href); notFoundContainer.insertBefore(jannyButton, homePageLink.nextSibling); processing = false; }; const handleCharacterPages = () => { if (!shouldRunOnPage() || processing) { debugLog('Already processing or not target page - skipping character page'); return; } processing = true; debugLog('Processing character page'); const targetDiv = document.querySelector('.css-3f016y'); if (!targetDiv) { debugLog('Character page target div not found'); // Check if this is actually a 404 page first if (document.querySelector('.css-64aicp')) { debugLog('Found 404 page instead of character page'); handle404Page(); return; } if (retryCount < MAX_RETRIES) { debugLog(`Retrying character page check (${retryCount + 1}/${MAX_RETRIES})`); retryCount++; setTimeout(handleCharacterPages, 500); } else { debugLog('Max retries reached for character page check'); } processing = false; return; } if (targetDiv.querySelector('.jannyai-button')) { debugLog('JannyAI button already exists on character page'); processing = false; return; } const jannyButton = createJannyButton(window.location.href); const actionButtons = targetDiv.querySelector('.css-70qvj9, button'); if (actionButtons) { debugLog('Inserting JannyAI button before action buttons'); targetDiv.insertBefore(jannyButton, actionButtons); } else { debugLog('Prepending JannyAI button to target div'); targetDiv.prepend(jannyButton); } retryCount = 0; setTimeout(() => { processing = false; }, DEBOUNCE_DELAY); }; const handleChatListings = () => { if (!shouldRunOnPage() || processing) { debugLog('Already processing or not target page - skipping chat listings'); return; } processing = true; debugLog('Processing chat listings page'); const chatBoxes = document.querySelectorAll('.css-ch9mb'); if (chatBoxes.length === 0 && retryCount < MAX_RETRIES) { debugLog(`No chat boxes found, retrying (${retryCount + 1}/${MAX_RETRIES})`); retryCount++; setTimeout(handleChatListings, 500); processing = false; return; } debugLog(`Found ${chatBoxes.length} chat boxes`); chatBoxes.forEach((chatBox, index) => { const charPageButton = chatBox.nextElementSibling; if (charPageButton?.classList.contains('_characterPageButton_1gdr6_37') && !charPageButton.nextElementSibling?.classList.contains('jannyai-button')) { debugLog(`Injecting JannyAI button for chat box ${index + 1}`); const jannyButton = createJannyButton(charPageButton.href); charPageButton.parentNode.insertBefore(jannyButton, charPageButton.nextSibling); } }); retryCount = 0; setTimeout(() => { processing = false; }, DEBOUNCE_DELAY); }; const runObserver = () => { if (!shouldRunOnPage()) { debugLog('Not a target page - observer skipping'); return; } debugLog('Observer triggered - checking page state'); setTimeout(() => { if (document.querySelector('.css-64aicp')) { debugLog('404 page detected'); handle404Page(); } else if (window.location.pathname.includes('/characters/')) { debugLog('Character page detected'); handleCharacterPages(); } else if (window.location.pathname === '/my_chats') { debugLog('Chat listings page detected'); handleChatListings(); } else { debugLog('Unknown page type - no action taken'); } }, INITIAL_DELAY); }; // New history event handlers history.pushState = (f => function pushState() { debugLog('pushState detected - dispatching events'); const ret = f.apply(this, arguments); window.dispatchEvent(new Event('pushstate')); window.dispatchEvent(new Event('locationchange')); return ret; })(history.pushState); history.replaceState = (f => function replaceState() { debugLog('replaceState detected - dispatching events'); const ret = f.apply(this, arguments); window.dispatchEvent(new Event('replacestate')); window.dispatchEvent(new Event('locationchange')); return ret; })(history.replaceState); const observer = new MutationObserver((mutations) => { debugLog(`Mutation observed - ${mutations.length} changes`); if (mutations.some(m => m.addedNodes.length > 0)) { debugLog('Added nodes detected - triggering runObserver'); runObserver(); } }); // Initial run debugLog('Starting initial observer run'); setTimeout(runObserver, INITIAL_DELAY); observer.observe(document.body, { childList: true, subtree: true }); window.addEventListener('locationchange', () => { debugLog('locationchange event received - resetting retries'); retryCount = 0; runObserver(); }); debugLog('JannyAI redirect script initialized'); })();