☰

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 tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら、通報はこちらへお寄せください
// ==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);
    }
})();