☰

Sleazy Fork is available in English.

JanitorAI → DataCat FINAL

On JanitorAI: grabs the bot name and opens DataCat. On DataCat: automatically pastes the name into the search bar.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Вам потребуется установить расширение, например Tampermonkey или Violentmonkey, чтобы установить этот скрипт.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         JanitorAI → DataCat FINAL
// @namespace    janitorai-datacat
// @version      1.2
// @description  On JanitorAI: grabs the bot name and opens DataCat. On DataCat: automatically pastes the name into the search bar.
// @author       You
// @match        *://janitorai.com/*
// @match        *://www.janitorai.com/*
// @match        *://*.janitorai.com/*
// @match        *://datacat.run/*
// @match        *://www.datacat.run/*
// @run-at       document-start
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const host = location.hostname.toLowerCase();
    const isJanitor = host === 'janitorai.com' || host === 'www.janitorai.com' || host.endsWith('.janitorai.com');
    const isDataCat = host === 'datacat.run' || host === 'www.datacat.run' || host.endsWith('.datacat.run');

    console.log('[JanitorAI → DataCat] Host:', host, '| Janitor:', isJanitor, '| DataCat:', isDataCat);

    /* =========================
       PART 1 — JANITOR AI
    ========================= */
    if (isJanitor) {
        const BUTTON_ID = 'jcc-datacat-button';
        let currentBotName = null;

        function cleanBotName(name) {
            if (!name) return null;
            return name
                .replace(/\s+/g, ' ')
                .replace(/\s*\|\s*JanitorAI.*$/i, '')
                .replace(/\s*-\s*JanitorAI.*$/i, '')
                .trim();
        }

        function findBotName() {
            const title = cleanBotName(document.title);
            if (
                title &&
                title.length > 1 &&
                !/^(janitorai|chat|home|similar characters)$/i.test(title)
            ) {
                return title;
            }

            const headings = document.querySelectorAll('h1, h2, [role="heading"]');
            for (const el of headings) {
                const text = cleanBotName(el.textContent);
                if (!text) continue;
                if (/^(chat|chats|characters|home|settings|search|similar characters|discover characters)$/i.test(text)) continue;
                if (text.length >= 3) return text;
            }
            return null;
        }

        function detectBotName() {
            const name = findBotName();
            if (name && name !== currentBotName) {
                currentBotName = name;
                console.log('[JanitorAI → DataCat] Bot found:', currentBotName);
                updateButton();
            }
            return currentBotName;
        }

        function updateButton() {
            const button = document.getElementById(BUTTON_ID);
            if (!button) return;

            if (currentBotName) {
                button.textContent = 'DataCat';
                button.style.background = '#16a34a';
                button.title = 'Open DataCat with: ' + currentBotName;
            } else {
                button.textContent = 'DataCat: searching...';
                button.style.background = '#6b7280';
                button.title = 'Waiting for bot name...';
            }
        }

        function createButton() {
            if (document.getElementById(BUTTON_ID)) return;
            if (!document.body) return;

            const button = document.createElement('button');
            button.id = BUTTON_ID;
            button.type = 'button';
            button.textContent = 'DataCat: searching...';
            button.style.cssText = `
                position: fixed;
                bottom: 20px;
                right: 20px;
                z-index: 2147483647;
                padding: 10px 16px;
                border: 0;
                border-radius: 8px;
                background: #6b7280;
                color: white;
                font-family: system-ui, -apple-system, sans-serif;
                font-size: 13px;
                font-weight: 700;
                cursor: pointer;
                box-shadow: 0 3px 12px rgba(0,0,0,.35);
            `;

            button.addEventListener('click', function () {
                const botName = detectBotName();
                if (!botName) {
                    alert('Could not find the bot name yet.\nWait for the chat to load and try again.');
                    return;
                }
                const url = 'https://datacat.run/characters/recent#q=' + encodeURIComponent(botName);
                window.open(url, '_blank');
            });

            document.body.appendChild(button);
            console.log('[JanitorAI → DataCat] Button created');
            updateButton();
        }

        // SPA navigation
        let lastUrl = location.href;
        function checkUrlChange() {
            if (location.href === lastUrl) return;
            lastUrl = location.href;
            currentBotName = null;
            updateButton();
            setTimeout(detectBotName, 300);
            setTimeout(detectBotName, 1000);
            setTimeout(detectBotName, 2500);
        }

        const originalPushState = history.pushState;
        history.pushState = function () {
            const result = originalPushState.apply(this, arguments);
            checkUrlChange();
            return result;
        };

        const originalReplaceState = history.replaceState;
        history.replaceState = function () {
            const result = originalReplaceState.apply(this, arguments);
            checkUrlChange();
            return result;
        };

        window.addEventListener('popstate', checkUrlChange);

        const observer = new MutationObserver(function () {
            detectBotName();
            createButton();
        });

        function startJanitor() {
            if (!document.body) {
                setTimeout(startJanitor, 50);
                return;
            }
            createButton();
            observer.observe(document.documentElement, { childList: true, subtree: true });
            setTimeout(detectBotName, 100);
            setTimeout(detectBotName, 500);
            setTimeout(detectBotName, 1000);
            setTimeout(detectBotName, 2000);
            setTimeout(detectBotName, 4000);
        }

        startJanitor();
    }

    /* =========================
       PART 2 — DATACAT
    ========================= */
    if (isDataCat) {
        function getQueryFromHash() {
            const hash = location.hash || '';
            const match = hash.match(/[#&]q=([^&]+)/);
            if (!match) return null;
            try {
                return decodeURIComponent(match[1].replace(/\+/g, ' '));
            } catch (e) {
                return match[1];
            }
        }

        function findSearchInput() {
            const inputs = Array.from(document.querySelectorAll('input'));
            const candidates = inputs.filter(function (el) {
                const ph = (el.placeholder || '').toLowerCase();
                const type = (el.type || '').toLowerCase();
                return type === 'search' || type === 'text' ||
                    ph.indexOf('search') !== -1 ||
                    ph.indexOf('filter') !== -1 ||
                    ph.indexOf('character') !== -1;
            });

            return candidates.find(function (el) {
                const ph = (el.placeholder || '').toLowerCase();
                return ph.indexOf('search') !== -1 || ph.indexOf('character') !== -1;
            }) || candidates[0] || null;
        }

        function setReactValue(input, value) {
            const descriptor = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value');
            if (descriptor && descriptor.set) {
                descriptor.set.call(input, value);
            } else {
                input.value = value;
            }
            input.dispatchEvent(new Event('input', { bubbles: true }));
            input.dispatchEvent(new Event('change', { bubbles: true }));
        }

        function tryPaste() {
            const query = getQueryFromHash();
            if (!query) return true;

            const input = findSearchInput();
            if (!input) return false;

            input.focus();
            setReactValue(input, query);

            ['keydown', 'keypress', 'keyup'].forEach(function (type) {
                input.dispatchEvent(new KeyboardEvent(type, {
                    key: 'Enter',
                    code: 'Enter',
                    keyCode: 13,
                    which: 13,
                    bubbles: true
                }));
            });

            history.replaceState(null, '', location.pathname + location.search);
            console.log('[JanitorAI → DataCat] Pasted into DataCat:', query);
            return true;
        }

        let attempts = 0;
        const interval = setInterval(function () {
            attempts++;
            if (tryPaste() || attempts > 30) {
                clearInterval(interval);
            }
        }, 400);
    }
})();