Injectionche

Integre vos collections de stickers personnels dans le picker natif d'Onche.org

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

Advertisement:

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

Advertisement:

// ==UserScript==
// @name         Injectionche
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Integre vos collections de stickers personnels dans le picker natif d'Onche.org
// @author       SilverWolf
// @match        https://onche.org/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    // =========================================================
    //  CONFIG
    // =========================================================
    var collectionsCache           = null;
    var stickersCacheParCollection = {};

    // =========================================================
    //  MutationObserver — injecter l'onglet quand le picker s'ouvre
    // =========================================================

    var _debounceObs = null;
    var observerPicker = new MutationObserver(function() {
        if (_debounceObs) return;
        _debounceObs = setTimeout(function() {
            _debounceObs = null;
            tenterInjectionOnglet();
        }, 100);
    });

    function tenterInjectionOnglet() {
        if (document.getElementById('sp-tab-mes-collections')) return;

        var tabsBar = null;
        var allTabs = document.querySelectorAll('#tooltip .tabs, .tabs');
        for (var i = 0; i < allTabs.length; i++) {
            if (allTabs[i].textContent.indexOf('Stickers') !== -1) {
                tabsBar = allTabs[i];
                break;
            }
        }
        if (!tabsBar) return;

        var tab = document.createElement('button');
        tab.type        = 'button';
        tab.id          = 'sp-tab-mes-collections';
        tab.textContent = '⭐ Mes collections';
        tab.style.cssText = 'padding:6px 10px;background:none;border:none;border-bottom:2px solid transparent;color:#aaa;cursor:pointer;font-size:12px;font-weight:bold;white-space:nowrap;transition:color 0.2s,border-color 0.2s';

        tab.addEventListener('mouseenter', function() { this.style.color = '#4a90d9'; });
        tab.addEventListener('mouseleave', function() {
            if (this.getAttribute('data-active') !== '1') { this.style.color = '#aaa'; this.style.borderBottomColor = 'transparent'; }
        });
        tab.addEventListener('click', function() {
            tabsBar.querySelectorAll('button, [class*="tab"]').forEach(function(t) {
                t.style.borderBottomColor = 'transparent';
                t.style.color = '#aaa';
                t.removeAttribute('data-active');
            });
            tab.style.color            = '#4a90d9';
            tab.style.borderBottomColor = '#4a90d9';
            tab.setAttribute('data-active', '1');
            afficherPanneauCollections();
        });

        tabsBar.appendChild(tab);
    }

    observerPicker.observe(document.body, { childList: true, subtree: true });

    // =========================================================
    //  Panneau collections
    // =========================================================

    function afficherPanneauCollections() {
        var tabBtn = document.getElementById('sp-tab-mes-collections');
        if (!tabBtn) return;

        var tooltip = document.getElementById('tooltip') || tabBtn.closest('.with-margin');
        if (!tooltip) return;

        var ancien = document.getElementById('sp-panneau-collections');
        if (ancien) ancien.remove();

        var panneau      = document.createElement('div');
        panneau.id       = 'sp-panneau-collections';
        var rect         = tooltip.getBoundingClientRect();
        panneau.style.cssText = [
            'position:fixed',
            'top:'    + (rect.top    + 40) + 'px',
            'left:'   +  rect.left         + 'px',
            'width:'  +  rect.width        + 'px',
            'height:' + (rect.height - 40) + 'px',
            'background:#0f1923',
            'z-index:99999',
            'overflow-y:auto',
            'padding:8px',
            'box-sizing:border-box',
            'border-top:1px solid #2c3e50',
            'border-radius:0 0 8px 8px'
        ].join(';');

        panneau.innerHTML = '<div id="sp-contenu-collections"><div style="color:#aaa;text-align:center;padding:20px;font-size:12px">Chargement de tes collections...</div></div>';
        tooltip.appendChild(panneau);

        // Fermer quand on clique sur un onglet natif
        tooltip.querySelectorAll('.tab').forEach(function(t) {
            t.addEventListener('click', function() {
                panneau.remove();
                tabBtn.style.color            = '#aaa';
                tabBtn.style.borderBottomColor = 'transparent';
                tabBtn.removeAttribute('data-active');
            });
        });

        chargerCollections(function(collections) {
            afficherListeCollections(collections);
        });
    }

    // =========================================================
    //  Charger les collections
    // =========================================================

    function chargerCollections(callback) {
        if (collectionsCache) { callback(collectionsCache); return; }
        var xhr = new XMLHttpRequest();
        xhr.open('GET', 'https://onche.org/stickers');
        xhr.withCredentials = true;
        xhr.onload = function() {
            var doc         = new DOMParser().parseFromString(xhr.responseText, 'text/html');
            var collections = [];
            doc.querySelectorAll('a[href*="/stickers/collection/"]').forEach(function(lien) {
                var href = lien.getAttribute('href');
                var mId  = href.match(/\/collection\/(\d+)/);
                if (!mId) return;
                var id  = mId[1];
                var nom = lien.textContent.trim() || 'Collection ' + id;
                var imgSrc = null;
                var imgs   = lien.querySelectorAll('img[src*="cloud.onche"]');
                for (var i = 0; i < imgs.length; i++) {
                    if (!imgs[i].className || imgs[i].className.indexOf('avatar') === -1) {
                        imgSrc = imgs[i].getAttribute('src');
                        break;
                    }
                }
                if (!collections.find(function(c) { return c.id === id; })) {
                    collections.push({ id: id, nom: nom, imgSrc: imgSrc });
                }
            });
            collectionsCache = collections;
            callback(collections);
        };
        xhr.onerror = function() { callback([]); };
        xhr.send();
    }

    // =========================================================
    //  Afficher la liste des collections
    // =========================================================

    function afficherListeCollections(collections) {
        var contenu = document.getElementById('sp-contenu-collections');
        if (!contenu) return;
        if (collections.length === 0) { afficherRecherche(contenu); return; }
        contenu.innerHTML = '';

        var recherche       = document.createElement('input');
        recherche.type        = 'text';
        recherche.placeholder = 'Chercher dans mes stickers...';
        recherche.style.cssText = 'width:100%;padding:5px 8px;background:#0d1b2a;color:#eee;border:1px solid #2c3e50;border-radius:4px;font-size:11px;box-sizing:border-box;margin-bottom:8px;outline:none';
        var _rechercheTimeout = null;
        recherche.addEventListener('input', function() {
            clearTimeout(_rechercheTimeout);
            var q = this.value.trim();
            if (q.length > 1) {
                _rechercheTimeout = setTimeout(function() { rechercherStickers(q, contenu); }, 400);
            } else {
                afficherGrilleCollections(collections, contenu);
            }
        });
        contenu.appendChild(recherche);
        afficherGrilleCollections(collections, contenu);
    }

    function afficherGrilleCollections(collections, contenu) {
        var ancienne = contenu.querySelector('#sp-grille-collections');
        if (ancienne) ancienne.remove();
        var grille   = document.createElement('div');
        grille.id    = 'sp-grille-collections';
        grille.style.cssText = 'display:grid;grid-template-columns:repeat(3,1fr);gap:6px';
        collections.forEach(function(col) {
            var item = document.createElement('div');
            item.style.cssText = 'background:#0d1b2a;border:1px solid #2c3e50;border-radius:6px;padding:6px;cursor:pointer;text-align:center;transition:border-color 0.2s';
            if (col.imgSrc) {
                var img = document.createElement('img');
                img.src = col.imgSrc;
                img.style.cssText = 'width:40px;height:40px;object-fit:cover;border-radius:4px;display:block;margin:0 auto 3px';
                item.appendChild(img);
            }
            var span = document.createElement('span');
            span.style.cssText = 'font-size:10px;color:#eee;word-break:break-word';
            span.textContent   = col.nom;
            item.appendChild(span);
            item.addEventListener('mouseenter', function() { this.style.borderColor = '#4a90d9'; });
            item.addEventListener('mouseleave', function() { this.style.borderColor = '#2c3e50'; });
            item.addEventListener('click', function() { chargerStickersCollection(col, contenu); });
            grille.appendChild(item);
        });
        contenu.appendChild(grille);
    }

    // =========================================================
    //  Charger les stickers d'une collection
    // =========================================================

    function chargerStickersCollection(collection, contenu) {
        if (stickersCacheParCollection[collection.id]) {
            afficherStickers(stickersCacheParCollection[collection.id], collection.nom, contenu);
            return;
        }
        var ancien = contenu.querySelector('#sp-grille-collections, #sp-grille-stickers');
        if (ancien) ancien.style.opacity = '0.5';
        var xhr = new XMLHttpRequest();
        xhr.open('GET', 'https://onche.org/stickers/collection/' + collection.id);
        xhr.withCredentials = true;
        xhr.onload = function() {
            var doc      = new DOMParser().parseFromString(xhr.responseText, 'text/html');
            var stickers = [];
            doc.querySelectorAll('img[src*="cloud.onche.org"]').forEach(function(img) {
                if (img.className && img.className.indexOf('avatar') !== -1) return;
                var alt  = img.getAttribute('alt') || '';
                var src  = img.getAttribute('src') || '';
                var mNom = alt.match(/^:(.+):$/);
                if (!mNom || !src) return;
                var name = mNom[1];
                if (src.indexOf('/128') === -1 && src.indexOf('!') !== -1) {
                    src = src.split('!')[0] + '!' + src.split('!')[1].split('/')[0] + '/128';
                }
                if (!stickers.find(function(s) { return s.name === name; })) {
                    stickers.push({ name: name, image: src });
                }
            });
            if (stickers.length === 0) {
                rechercherStickersCollection(collection.nom, function(results) {
                    stickersCacheParCollection[collection.id] = results;
                    afficherStickers(results, collection.nom, contenu);
                });
                return;
            }
            stickersCacheParCollection[collection.id] = stickers;
            afficherStickers(stickers, collection.nom, contenu);
        };
        xhr.onerror = function() { afficherStickers([], collection.nom, contenu); };
        xhr.send();
    }

    // =========================================================
    //  Recherche API
    // =========================================================

    function rechercherStickers(q, contenu) {
        var ancien = contenu.querySelector('#sp-grille-collections, #sp-grille-stickers');
        if (ancien) ancien.remove();
        var loader = document.createElement('div');
        loader.id  = 'sp-grille-stickers';
        loader.style.cssText = 'color:#aaa;text-align:center;padding:15px;font-size:12px';
        loader.textContent   = 'Recherche...';
        contenu.appendChild(loader);
        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'https://onche.org/stickers/search');
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.withCredentials = true;
        xhr.onload  = function() {
            try { afficherStickers(JSON.parse(xhr.responseText), 'Resultats pour "' + q + '"', contenu); }
            catch(e) { afficherStickers([], 'Aucun resultat', contenu); }
        };
        xhr.onerror = function() { afficherStickers([], 'Erreur reseau', contenu); };
        xhr.send('q=' + encodeURIComponent(q));
    }

    function rechercherStickersCollection(nom, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'https://onche.org/stickers/search');
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.withCredentials = true;
        xhr.onload  = function() { try { callback(JSON.parse(xhr.responseText)); } catch(e) { callback([]); } };
        xhr.onerror = function() { callback([]); };
        xhr.send('q=' + encodeURIComponent(nom));
    }

    function afficherRecherche(contenu) {
        contenu.innerHTML = '';
        var info = document.createElement('div');
        info.style.cssText = 'color:#aaa;font-size:11px;margin-bottom:8px;text-align:center';
        info.textContent   = 'Recherche dans tous tes stickers :';
        contenu.appendChild(info);
        var recherche       = document.createElement('input');
        recherche.type        = 'text';
        recherche.placeholder = 'Nom du sticker...';
        recherche.style.cssText = 'width:100%;padding:5px 8px;background:#0d1b2a;color:#eee;border:1px solid #2c3e50;border-radius:4px;font-size:11px;box-sizing:border-box;margin-bottom:8px;outline:none';
        var timeout = null;
        recherche.addEventListener('input', function() {
            clearTimeout(timeout);
            var q = this.value.trim();
            if (q.length > 1) timeout = setTimeout(function() { rechercherStickers(q, contenu); }, 400);
        });
        contenu.appendChild(recherche);
    }

    // =========================================================
    //  Grille de stickers
    // =========================================================

    function afficherStickers(stickers, titre, contenu) {
        var ancienne = contenu.querySelector('#sp-grille-stickers, #sp-grille-collections');
        if (ancienne) ancienne.remove();

        var header = document.createElement('div');
        header.id  = 'sp-grille-stickers';
        header.style.cssText = 'margin-top:4px';

        var headerBar = document.createElement('div');
        headerBar.style.cssText = 'display:flex;align-items:center;gap:6px;margin-bottom:8px';

        var btnRetour = document.createElement('button');
        btnRetour.type        = 'button';
        btnRetour.id          = 'sp-btn-retour';
        btnRetour.textContent = '← Retour';
        btnRetour.style.cssText = 'background:none;border:1px solid #2c3e50;color:#aaa;border-radius:3px;cursor:pointer;padding:2px 6px;font-size:10px';
        btnRetour.addEventListener('click', function() {
            chargerCollections(function(cols) { header.remove(); afficherGrilleCollections(cols, contenu); });
        });

        var titreSpan = document.createElement('span');
        titreSpan.style.cssText = 'color:#4a90d9;font-size:11px;font-weight:bold';
        titreSpan.textContent   = titre + ' (' + stickers.length + ')';

        headerBar.appendChild(btnRetour);
        headerBar.appendChild(titreSpan);
        header.appendChild(headerBar);

        if (stickers.length === 0) {
            var vide = document.createElement('div');
            vide.style.cssText = 'color:#aaa;text-align:center;padding:15px;font-size:11px';
            vide.textContent   = 'Aucun sticker trouve';
            header.appendChild(vide);
        } else {
            var grille = document.createElement('div');
            grille.style.cssText = 'display:grid;grid-template-columns:repeat(4,1fr);gap:5px';
            stickers.forEach(function(s) {
                var item = document.createElement('div');
                item.style.cssText = 'cursor:pointer;border-radius:4px;padding:2px;border:2px solid transparent;transition:border-color 0.15s;text-align:center';
                item.title = s.name;
                var sImg = document.createElement('img');
                sImg.src   = s.image;
                sImg.style.cssText = 'width:100%;max-width:56px;height:56px;object-fit:contain;border-radius:3px';
                sImg.loading = 'lazy';
                sImg.alt     = s.name;
                var sNom = document.createElement('div');
                sNom.style.cssText = 'font-size:9px;color:#888;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:64px';
                sNom.textContent   = s.name;
                item.appendChild(sImg);
                item.appendChild(sNom);
                item.addEventListener('mouseenter', function() { this.style.borderColor = '#4a90d9'; });
                item.addEventListener('mouseleave', function() { this.style.borderColor = 'transparent'; });
                item.addEventListener('click', function() { insererSticker(s); });
                grille.appendChild(item);
            });
            header.appendChild(grille);
        }
        contenu.appendChild(header);
    }

    // =========================================================
    //  Inserer le sticker dans le textarea
    // =========================================================

    function insererSticker(sticker) {
        // Cacher le panneau
        var panneau = document.getElementById('sp-panneau-collections');
        if (panneau) panneau.style.display = 'none';

        // Format confirme : :nom_sticker: dans textarea[name="message"]
        var textarea = document.querySelector('textarea[name="message"]');
        if (!textarea) return;

        var val    = textarea.value || '';
        var insert = (val && !val.endsWith(' ') ? ' ' : '') + ':' + sticker.name + ':';
        textarea.value = val + insert;
        textarea.dispatchEvent(new Event('input',  { bubbles: true }));
        textarea.dispatchEvent(new Event('change', { bubbles: true }));
        textarea.focus();
        textarea.setSelectionRange(textarea.value.length, textarea.value.length);
    }

})();