☰

JanitorAI → DataCat FINAL

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

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==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);
    }
})();