☰

JanitorAI → DataCat FINAL

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

Terá de instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

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

Terá de instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Terá de instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Terá de instalar uma extensão como Tampermonkey para instalar este script.

Terá de instalar uma extensão de gestão de scripts de utilizador para instalar este script.

(Já tenho um gestor de scripts de utilizador, deixe-me instalá-lo!)

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

(Já tenho um gestor de estilos de utilizador, deixe-me instalá-lo!)

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