Horlonche

Planificateur de posts, sniper de topics et historique pour Onche.org

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

Advertisement:

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

Advertisement:

// ==UserScript==
// @name         Horlonche
// @namespace    http://tampermonkey.net/
// @version      14.0
// @description  Planificateur de posts, sniper de topics et historique pour Onche.org
// @author       Musclor1000
// @match        https://onche.org/topic/*
// @match        https://onche.org/forum/*
// @match        https://onche.org/
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    var estForum = window.location.pathname.indexOf('/forum/') === 0;

    var DELAI_MIN_POSTS = 8000;
    var dernierPost     = 0;
    var postEnCours     = false;
    var fileAttente = [];
    var idCounter   = 0;

    function sauvegarderFile() {
        var data = fileAttente.map(function(item) {
            return { id: item.id, mode: item.mode, titre: item.titre, message: item.message, tsEcheance: item.tsEcheance, label: item.label, cibleInfo: item.cibleInfo || null, pageUrl: item.pageUrl || null };
        }).filter(function(item) { return item.tsEcheance > Date.now(); });
        sessionStorage.setItem('horlonche_file', JSON.stringify(data));
    }

    function restaurerFile() {
        var raw = sessionStorage.getItem('horlonche_file');
        if (!raw) return;
        try {
            var data = JSON.parse(raw);
            for (var i = 0; i < data.length; i++) {
                var item = data[i];
                if (item.tsEcheance > Date.now()) { if (item.id >= idCounter) idCounter = item.id; ajouterFile(item); }
            }
        } catch(e) {}
    }

    var vueActive  = 'liste';
    var modeChoisi = null;

    var panel = document.createElement('div');
    panel.style.cssText = 'position:fixed;bottom:20px;right:20px;background:#1e2a3a;border:1px solid #4a90d9;border-radius:8px;padding:14px;z-index:99999;width:270px;font-family:sans-serif;font-size:13px;color:#eee;box-shadow:0 4px 15px rgba(0,0,0,0.5)';

    panel.innerHTML = ''
        + '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">'
        + '<span style="font-weight:bold;font-size:14px;color:#4a90d9">Horlonche</span>'
        + '<div style="display:flex;gap:4px">'
        + '<button type="button" id="os-btn-sniper" title="Sniper de topic" style="background:none;border:1px solid #e74c3c;color:#e74c3c;border-radius:4px;width:24px;height:24px;cursor:pointer;font-size:13px;line-height:1;padding:0">🎯</button>'
        + '<button type="button" id="os-btn-watchdog" title="Watchdog" style="background:none;border:1px solid #e67e22;color:#e67e22;border-radius:4px;width:24px;height:24px;cursor:pointer;font-size:13px;line-height:1;padding:0">🐕</button>'
        + '<button type="button" id="os-btn-plus" title="Ajouter un post" style="background:none;border:1px solid #4a90d9;color:#4a90d9;border-radius:4px;width:24px;height:24px;cursor:pointer;font-size:16px;line-height:1;padding:0">+</button>'
        + '</div>'
        + '</div>'
        + '<div id="os-vue-liste">'
        + '<div id="os-file-vide" style="color:#aaa;font-size:12px;text-align:center;padding:10px 0">Aucun post programme.<br>Clique sur <b>+</b> pour en ajouter un.</div>'
        + '<div id="os-file-liste"></div>'
        + '<div id="os-cooldown" style="margin-top:6px;padding:5px 8px;background:#1a0000;border-left:3px solid #e74c3c;border-radius:3px;font-size:11px;color:#e74c3c;font-family:monospace">&#9888; Delai min entre posts : <b>8s</b> — Cloudflare</div>'
        + '</div>'
        + '<div id="os-vue-form" style="display:none">'
        + '<div id="os-etape1">'
        + '<div style="margin-bottom:8px;color:#aaa;font-size:12px">Que veux-tu faire ?</div>'
        + (estForum
            ? '<button type="button" class="os-mode-btn" data-mode="nouveau" style="width:100%;padding:7px;margin-bottom:6px;background:#2c3e50;color:#eee;border:1px solid #4a90d9;border-radius:4px;cursor:pointer;text-align:left">📝 Creer un nouveau topic</button>'
            : '<button type="button" class="os-mode-btn" data-mode="topic" style="width:100%;padding:7px;margin-bottom:6px;background:#2c3e50;color:#eee;border:1px solid #4a90d9;border-radius:4px;cursor:pointer;text-align:left">💬 Repondre dans le topic</button>'
              + '<button type="button" class="os-mode-btn" data-mode="citation" style="width:100%;padding:7px;margin-bottom:6px;background:#2c3e50;color:#eee;border:1px solid #4a90d9;border-radius:4px;cursor:pointer;text-align:left">↩ Citer / repondre a un message</button>'
          )
        + '<button type="button" id="os-btn-annuler-form" style="width:100%;padding:5px;margin-top:4px;background:#2c3e50;color:#aaa;border:1px solid #555;border-radius:4px;cursor:pointer;font-size:12px">← Retour</button>'
        + '</div>'
        + '<div id="os-etape2" style="display:none">'
        + '<div id="os-label-mode" style="margin-bottom:10px;padding:5px 8px;background:#0d1b2a;border-radius:4px;font-size:12px;color:#4a90d9"></div>'
        + '<div id="os-instruction-citation" style="display:none;margin-bottom:10px;padding:6px;background:#2c1a00;border:1px solid #f39c12;border-radius:4px;font-size:11px;color:#f39c12">⚠ Clique sur la fleche du message AVANT de cliquer "Ajouter"</div>'
        + '<div id="os-champ-titre" style="display:none">'
        + '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">'
        + '<label>Titre du topic</label>'
        + '<label style="font-size:10px;color:#f39c12;cursor:pointer;display:flex;align-items:center;gap:4px"><input type="checkbox" id="os-chk-troll" style="cursor:pointer"> Fantome</label>'
        + '</div>'
        + '<input id="os-titre" type="text" placeholder="Titre..." style="width:100%;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #4a90d9;border-radius:4px;margin-bottom:4px;box-sizing:border-box">'
        + '<div id="os-titre-troll-info" style="display:none;padding:5px 7px;background:#0d1b2a;border-left:3px solid #f39c12;border-radius:3px;font-size:10px;color:#f39c12;margin-bottom:6px">Onche recevra ton emoji seul — slug vide garanti.<br>Laisse vide pour un topic sans titre visible.<br>Ton titre sera poste en 1er message automatiquement.</div>'
        + '</div>'
        + '<div id="os-champ-url-cible" style="margin-bottom:8px">'
        + '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:3px">'
        + '<label style="font-size:12px;color:#aaa">URL cible (optionnel)</label>'
        + '<button type="button" id="os-btn-detecter-url" style="padding:2px 6px;background:none;border:1px solid #4a90d9;color:#4a90d9;border-radius:3px;cursor:pointer;font-size:10px">Auto</button>'
        + '</div>'
        + '<input id="os-url-cible" type="text" placeholder="Laisser vide = page courante" style="width:100%;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #2c3e50;border-radius:4px;font-size:11px;box-sizing:border-box;margin-bottom:4px">'
        + '<div id="os-url-info" style="display:none;padding:5px 7px;background:#0d1b2a;border-left:3px solid #f39c12;border-radius:3px;font-size:10px;color:#f39c12"></div>'
        + '</div>'
        + '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">'
        + '<label>Heure (HH:MM:SS)</label>'
        + '<label style="font-size:11px;color:#aaa;cursor:pointer;display:flex;align-items:center;gap:4px"><input type="checkbox" id="os-chk-jour" style="cursor:pointer"> Jour</label>'
        + '</div>'
        + '<input id="os-heure" type="time" step="1" style="width:100%;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #4a90d9;border-radius:4px;margin-bottom:6px;box-sizing:border-box">'
        + '<div id="os-champ-jour" style="display:none;margin-bottom:6px"><input id="os-jour" type="date" style="width:100%;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #4a90d9;border-radius:4px;box-sizing:border-box"></div>'
        + '<label style="display:block;margin-bottom:4px">Message</label>'
        + '<textarea id="os-message" rows="3" style="width:100%;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #4a90d9;border-radius:4px;margin-bottom:6px;box-sizing:border-box;resize:vertical"></textarea>'
        + '<button type="button" id="os-btn-preview" style="width:100%;padding:5px;background:#2c3e50;color:#aaa;border:1px solid #4a90d9;border-radius:4px;cursor:pointer;font-size:12px;margin-bottom:8px">👁 Previsualiser</button>'
        + '<button type="button" id="os-btn-start" style="width:100%;padding:7px;background:#4a90d9;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;margin-bottom:6px">Ajouter a la file</button>'
        + '<button type="button" id="os-btn-back" style="width:100%;padding:5px;background:#2c3e50;color:#aaa;border:1px solid #4a90d9;border-radius:4px;cursor:pointer;font-size:12px">← Retour</button>'
        + '</div>'
        + '</div>'
        + '<div id="os-vue-sniper" style="display:none">'
        + '<div style="margin-bottom:8px;color:#e74c3c;font-weight:bold;font-size:13px">🎯 Sniper de topic</div>'
        + '<label style="display:block;margin-bottom:4px;font-size:12px">Forums a scanner</label>'
        + '<div id="os-sniper-forums-liste" style="margin-bottom:4px"></div>'
        + '<div style="display:flex;gap:4px;margin-bottom:8px">'
        + '<input id="os-sniper-url-input" type="text" placeholder="https://onche.org/forum/..." style="flex:1;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #e74c3c;border-radius:4px;font-size:11px;box-sizing:border-box">'
        + '<button type="button" id="os-sniper-url-add" style="padding:5px 8px;background:#e74c3c;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px">+</button>'
        + '</div>'
        + '<label style="display:block;margin-bottom:4px;font-size:12px">Mots cles (separes par virgules)</label>'
        + '<input id="os-sniper-mot" type="text" placeholder="ex: crypto, bonjour..." style="width:100%;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #e74c3c;border-radius:4px;margin-bottom:8px;box-sizing:border-box">'
        + '<label style="display:block;margin-bottom:4px;font-size:12px">Message a poster</label>'
        + '<textarea id="os-sniper-message" rows="3" style="width:100%;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #e74c3c;border-radius:4px;margin-bottom:8px;box-sizing:border-box;resize:vertical"></textarea>'
        + '<label style="display:block;margin-bottom:4px;font-size:12px">Intervalle de scan (secondes)</label>'
        + '<input id="os-sniper-intervalle" type="number" value="10" min="5" max="60" style="width:100%;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #e74c3c;border-radius:4px;margin-bottom:8px;box-sizing:border-box">'
        + '<button type="button" id="os-sniper-start" style="width:100%;padding:7px;background:#e74c3c;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;margin-bottom:6px">Activer le sniper</button>'
        + '<button type="button" id="os-sniper-stop" style="display:none;width:100%;padding:7px;background:#c0392b;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;margin-bottom:6px">Desactiver</button>'
        + '<div style="display:flex;gap:4px">'
        + '<button type="button" id="os-sniper-retour" style="flex:1;padding:5px;background:#2c3e50;color:#aaa;border:1px solid #555;border-radius:4px;cursor:pointer;font-size:12px">← Retour</button>'
        + '<button type="button" id="os-sniper-historique-btn" style="padding:5px 8px;background:#2c3e50;color:#f39c12;border:1px solid #f39c12;border-radius:4px;cursor:pointer;font-size:12px">📋</button>'
        + '</div>'
        + '<div id="os-sniper-status" style="margin-top:8px;font-size:11px;color:#aaa;text-align:center"></div>'
        + '<div id="os-sniper-log" style="margin-top:6px;max-height:80px;overflow-y:auto;font-size:10px;color:#666"></div>'
        + '</div>'
        + '<div id="os-vue-watchdog" style="display:none">'
        + '<div id="os-wd-vue-config">'
        + '<div style="margin-bottom:8px;color:#e67e22;font-weight:bold;font-size:13px">🐕 Watchdog</div>'
        + '<label style="display:block;margin-bottom:4px;font-size:12px">Pseudos a surveiller</label>'
        + '<div id="os-wd-pseudos-liste" style="margin-bottom:4px"></div>'
        + '<div style="display:flex;gap:4px;margin-bottom:8px">'
        + '<input id="os-wd-pseudo-input" type="text" placeholder="Pseudo Onche..." style="flex:1;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #e67e22;border-radius:4px;font-size:11px;box-sizing:border-box">'
        + '<button type="button" id="os-wd-pseudo-add" style="padding:5px 8px;background:#e67e22;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px">+</button>'
        + '</div>'
        + '<label style="display:block;margin-bottom:4px;font-size:12px">Forums a surveiller</label>'
        + '<div id="os-wd-forums-liste" style="margin-bottom:4px"></div>'
        + '<div style="display:flex;gap:4px;margin-bottom:8px">'
        + '<input id="os-wd-forum-input" type="text" placeholder="https://onche.org/forum/..." style="flex:1;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #e67e22;border-radius:4px;font-size:11px;box-sizing:border-box">'
        + '<button type="button" id="os-wd-forum-add" style="padding:5px 8px;background:#e67e22;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px">+</button>'
        + '</div>'
        + '<label style="display:block;margin-bottom:4px;font-size:12px">Message auto (optionnel)</label>'
        + '<textarea id="os-wd-message" rows="2" placeholder="Laisser vide = pas de reponse auto" style="width:100%;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #e67e22;border-radius:4px;margin-bottom:8px;box-sizing:border-box;resize:vertical;font-size:12px"></textarea>'
        + '<label style="display:block;margin-bottom:4px;font-size:12px">Intervalle de scan (secondes)</label>'
        + '<input id="os-wd-intervalle" type="number" value="15" min="5" max="120" style="width:100%;padding:5px;background:#0d1b2a;color:#eee;border:1px solid #e67e22;border-radius:4px;margin-bottom:8px;box-sizing:border-box">'
        + '<div style="display:flex;gap:4px;margin-bottom:6px">'
        + '<button type="button" id="os-wd-start" style="flex:1;padding:7px;background:#e67e22;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px">▶ Activer</button>'
        + '<button type="button" id="os-wd-stop" style="display:none;flex:1;padding:7px;background:#c0392b;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px">⏹ Stop</button>'
        + '<button type="button" id="os-wd-scan-now" style="flex:1;padding:7px;background:#2c3e50;color:#e67e22;border:1px solid #e67e22;border-radius:4px;cursor:pointer;font-size:12px">🔍 Scan</button>'
        + '</div>'
        + '<div id="os-wd-status" style="margin-bottom:4px;font-size:11px;color:#aaa;text-align:center"></div>'
        + '<div style="display:flex;gap:4px;margin-top:6px">'
        + '<button type="button" id="os-wd-retour" style="flex:1;padding:5px;background:#2c3e50;color:#aaa;border:1px solid #555;border-radius:4px;cursor:pointer;font-size:12px">← Retour</button>'
        + '<button type="button" id="os-wd-voir-topics" style="flex:1;padding:5px;background:#2c3e50;color:#e67e22;border:1px solid #e67e22;border-radius:4px;cursor:pointer;font-size:12px">📋 Topics</button>'
        + '<button type="button" id="os-wd-historique-btn" style="padding:5px 8px;background:#2c3e50;color:#f39c12;border:1px solid #f39c12;border-radius:4px;cursor:pointer;font-size:12px">📋</button>'
        + '</div>'
        + '</div>'
        + '<div id="os-wd-vue-topics" style="display:none">'
        + '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">'
        + '<span style="font-weight:bold;font-size:13px;color:#e67e22">📋 Topics detectes</span>'
        + '<button type="button" id="os-wd-topics-retour" style="padding:3px 8px;background:#2c3e50;color:#aaa;border:1px solid #555;border-radius:3px;cursor:pointer;font-size:11px">← Retour</button>'
        + '</div>'
        + '<div id="os-wd-topics-liste" style="max-height:220px;overflow-y:auto;margin-bottom:8px;background:#0d1b2a;border-radius:4px;padding:4px">'
        + '<span style="color:#666;font-size:10px">Lancer le scan pour voir les topics...</span>'
        + '</div>'
        + '<div id="os-wd-log" style="max-height:50px;overflow-y:auto;font-size:10px;color:#666;margin-bottom:6px"></div>'
        + '<div style="display:flex;gap:4px">'
        + '<button type="button" id="os-wd-poster-selection" style="flex:1;padding:6px;background:#e67e22;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:11px">📤 Selection</button>'
        + '<button type="button" id="os-wd-poster-tous" style="flex:1;padding:6px;background:#c0392b;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:11px">📤 Tous</button>'
        + '</div>'
        + '</div>'
        + '</div>'
        + '<div id="os-vue-wd-historique" style="display:none">'
        + '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">'
        + '<span style="font-weight:bold;font-size:13px;color:#e67e22">🐕 Historique Watchdog</span>'
        + '<button type="button" id="os-wd-hist-clear" style="padding:3px 8px;background:none;border:1px solid #c0392b;color:#c0392b;border-radius:3px;cursor:pointer;font-size:11px">Vider</button>'
        + '</div>'
        + '<div id="os-wd-hist-liste" style="max-height:300px;overflow-y:auto"></div>'
        + '<button type="button" id="os-wd-hist-retour" style="width:100%;padding:5px;margin-top:8px;background:#2c3e50;color:#aaa;border:1px solid #555;border-radius:4px;cursor:pointer;font-size:12px">← Retour</button>'
        + '</div>'
        + '<div id="os-vue-historique" style="display:none">'
        + '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">'
        + '<span style="font-weight:bold;font-size:13px;color:#f39c12">📋 Historique</span>'
        + '<button type="button" id="os-historique-clear" style="padding:3px 8px;background:none;border:1px solid #c0392b;color:#c0392b;border-radius:3px;cursor:pointer;font-size:11px">Vider</button>'
        + '</div>'
        + '<div style="display:flex;gap:4px;margin-bottom:8px">'
        + '<button type="button" id="os-hist-tab-tracking" style="flex:1;padding:5px;background:#4a90d9;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:11px">Tracking (0)</button>'
        + '<button type="button" id="os-hist-tab-posting" style="flex:1;padding:5px;background:#2c3e50;color:#aaa;border:1px solid #2ecc71;border-radius:4px;cursor:pointer;font-size:11px">Posting (0)</button>'
        + '</div>'
        + '<div id="os-historique-liste" style="max-height:260px;overflow-y:auto"></div>'
        + '<button type="button" id="os-historique-retour" style="width:100%;padding:5px;margin-top:8px;background:#2c3e50;color:#aaa;border:1px solid #555;border-radius:4px;cursor:pointer;font-size:12px">← Retour</button>'
        + '</div>'
        + '</div>';

    document.body.appendChild(panel);
    restaurerFile();

    function normaliserUrlTopic(url) {
        if (!url) return window.location.href.split('#')[0];
        if (url.indexOf('http') !== 0) url = 'https://onche.org' + url;
        var ancre = '';
        var urlSansAncre = url;
        var hashIdx = url.indexOf('#');
        if (hashIdx !== -1) {
            var ancreVal = url.substring(hashIdx + 1);
            urlSansAncre = url.substring(0, hashIdx);
            if (/^message_\d+$/.test(ancreVal)) ancre = '#' + ancreVal;
            else if (ancreVal.indexOf('http') !== -1 || ancreVal.length > 50) ancre = '';
            else ancre = '#' + ancreVal;
        }
        var mId = urlSansAncre.match(/onche\.org\/topic\/(\d+)/);
        if (!mId) return url;
        var topicId = mId[1];
        var reste   = urlSansAncre.split('/topic/' + topicId)[1] || '';
        var parties = reste.split('/');
        var slug    = parties.length > 1 ? parties[1] : '';
        var page    = parties.length > 2 ? parties[2] : '';
        var slugVisible = slug;
        try { slugVisible = decodeURIComponent(slug); } catch(e) { return 'https://onche.org/topic/' + topicId + '/' + ancre; }
        slugVisible = slugVisible.replace(/[\u0000-\u001F\u007F-\u009F\u200B-\u200D\uFEFF\u00AD\u00A0\u2028\u2029]/g, '').trim();
        if (!slugVisible) return 'https://onche.org/topic/' + topicId + '/' + ancre;
        if (slugVisible.length > 120) slugVisible = slugVisible.substring(0, 120);
        var pageNum = 1;
        if (page) { pageNum = parseInt(page) || 1; if (pageNum < 1) pageNum = 1; if (pageNum > 9999) pageNum = 1; }
        return 'https://onche.org/topic/' + topicId + '/' + slugVisible + '/' + pageNum + ancre;
    }

    setTimeout(function() {
        if (window.location.href.indexOf('/topic/') === -1) return;
        try {
            var urlCorrigee = normaliserUrlTopic(window.location.href);
            if (urlCorrigee !== window.location.href && urlCorrigee !== window.location.href.split('#')[0]) window.location.replace(urlCorrigee);
        } catch(e) {}
        function corrigerLiens() {
            document.querySelectorAll('a[href*="/topic/"]').forEach(function(a) {
                var href = a.getAttribute('href');
                if (!href) return;
                var abs  = href.indexOf('http') === 0 ? href : 'https://onche.org' + href;
                var norm = normaliserUrlTopic(abs);
                if (norm !== abs) { a.setAttribute('href', norm); a.title = 'Lien corrige : ' + norm; }
            });
        }
        corrigerLiens();
        var debounce = null;
        new MutationObserver(function() { if (debounce) clearTimeout(debounce); debounce = setTimeout(corrigerLiens, 300); }).observe(document.body, { childList: true, subtree: false });
    }, 100);

    function detecterInfosTopic() {
        var url  = window.location.href;
        var info = document.getElementById('os-url-info');
        var inp  = document.getElementById('os-url-cible');
        if (!info || !inp) return;
        var m = url.match(/onche\.org\/topic\/(\d+)\/([^\/]*)\/?(\d*)/);
        if (!m) { info.style.display = 'none'; return; }
        var topicId = m[1], slug = m[2] || '', page = m[3] || '1';
        var messages = [];
        if (!slug || slug.length === 0) {
            messages.push('⚠ Troll #1 : slug vide !');
            inp.value = 'https://onche.org/topic/' + topicId + '/';
            var pc = parseInt(page) || 1;
            messages.push('Page ' + pc + ' — suivante : https://onche.org/topic/' + topicId + '//' + (pc + 1));
        }
        if (messages.length > 0) {
            info.style.display = 'block'; info.style.borderLeftColor = '#f39c12'; info.style.color = '#f39c12';
            info.innerHTML = messages.join('<br>');
        } else {
            var pn = parseInt(page) || 1;
            info.style.display = 'block'; info.style.borderLeftColor = '#2ecc71'; info.style.color = '#2ecc71';
            info.innerHTML = '✓ Normal — page ' + pn + ' — suivante : https://onche.org/topic/' + topicId + '/' + slug + '/' + (pn+1);
        }
    }

    function getUrlCible() {
        var inp = document.getElementById('os-url-cible');
        if (inp && inp.value.trim()) {
            var url = inp.value.trim();
            if (url.indexOf('https://onche.org/') !== 0) { inp.style.borderColor = '#e74c3c'; return window.location.href.split('#')[0]; }
            inp.style.borderColor = '#2ecc71';
            return url;
        }
        return window.location.href.split('#')[0];
    }

    setTimeout(function() { var btnAuto = document.getElementById('os-btn-detecter-url'); if (btnAuto) btnAuto.addEventListener('click', detecterInfosTopic); }, 350);

    var sniperTimer = null, sniperActif = false, sniperTopicsVus = {};
    try { var raw = sessionStorage.getItem('horlonche_sniper_vus'); if (raw) sniperTopicsVus = JSON.parse(raw); } catch(e) {}
    function sauvegarderTopicsVus() { try { sessionStorage.setItem('horlonche_sniper_vus', JSON.stringify(sniperTopicsVus)); } catch(e) {} }

    var sniperForums = ['https://onche.org/forum/1/blabla-general'];
    function afficherForums() {
        var liste = document.getElementById('os-sniper-forums-liste');
        if (!liste) return;
        liste.innerHTML = '';
        sniperForums.forEach(function(url, idx) {
            var ligne = document.createElement('div');
            ligne.style.cssText = 'display:flex;align-items:center;gap:4px;margin-bottom:4px;background:#0d1b2a;border:1px solid #3a2000;border-radius:4px;padding:3px 6px';
            var nom = url.replace('https://onche.org/forum/','').replace(/\/.*/,'');
            nom = ({1:'Blabla General',4:'Goulag',7:'Finance',8:'Jeux Video',9:'Autonomie',13:'Videoclub'})[nom] || url.split('/').pop();
            ligne.innerHTML = '<span style="flex:1;color:#eee;font-size:11px">' + nom + '</span><button type="button" data-idx="' + idx + '" style="background:none;border:none;color:#c0392b;cursor:pointer;font-size:13px;padding:0 2px">✕</button>';
            ligne.querySelector('button').addEventListener('click', function() { sniperForums.splice(parseInt(this.getAttribute('data-idx')),1); afficherForums(); });
            liste.appendChild(ligne);
        });
    }
    setTimeout(function() {
        afficherForums();
        document.getElementById('os-sniper-url-add').addEventListener('click', function() {
            var url = document.getElementById('os-sniper-url-input').value.trim();
            if (!url) return;
            if (url.indexOf('onche.org/forum/') === -1) { alert('URL invalide'); return; }
            if (sniperForums.indexOf(url) !== -1) { alert('Forum deja dans la liste'); return; }
            sniperForums.push(url); document.getElementById('os-sniper-url-input').value = ''; afficherForums();
        });
        document.getElementById('os-sniper-url-input').addEventListener('keydown', function(e) { if (e.key==='Enter') document.getElementById('os-sniper-url-add').click(); });
    }, 400);

    function ajouterHistorique(type, data) {
        var today = new Date().toLocaleDateString();
        var raw   = localStorage.getItem('horlonche_historique_' + today);
        var liste = [];
        try { if (raw) liste = JSON.parse(raw); } catch(e) {}
        liste.unshift(Object.assign({ heure: new Date().toLocaleTimeString(), type: type }, data));
        if (liste.length > 500) liste = liste.slice(0, 500);
        localStorage.setItem('horlonche_historique_' + today, JSON.stringify(liste));
    }

    var ongletHistActif = 'tracking';
    function chargerEntrees() {
        var entrees = [];
        for (var d = 0; d < 7; d++) {
            var date = new Date(); date.setDate(date.getDate() - d);
            var raw = localStorage.getItem('horlonche_historique_' + date.toLocaleDateString());
            try { if (raw) { var items = JSON.parse(raw); items.forEach(function(i){i.date=date.toLocaleDateString();}); entrees = entrees.concat(items); } } catch(e) {}
        }
        return entrees;
    }

    function afficherHistorique() {
        var liste = document.getElementById('os-historique-liste');
        if (!liste) return;
        liste.innerHTML = '';
        var entrees  = chargerEntrees();
        var filtrees = entrees.filter(function(e) { return ongletHistActif==='tracking' ? e.type==='trouve' : (e.type==='poste'||e.type==='erreur'); });
        var nbT = entrees.filter(function(e){return e.type==='trouve';}).length;
        var nbP = entrees.filter(function(e){return e.type==='poste'||e.type==='erreur';}).length;
        var btnT = document.getElementById('os-hist-tab-tracking');
        var btnP = document.getElementById('os-hist-tab-posting');
        if (btnT) btnT.textContent = 'Tracking (' + nbT + ')';
        if (btnP) btnP.textContent = 'Posting ('  + nbP + ')';
        if (filtrees.length === 0) { liste.innerHTML = '<div style="color:#aaa;text-align:center;padding:20px;font-size:12px">Aucune entree</div>'; return; }
        filtrees.forEach(function(item) {
            var ligne = document.createElement('div');
            ligne.style.cssText = 'padding:7px 8px;margin-bottom:5px;background:#0d1b2a;border-radius:4px;font-size:11px;' + (item.type==='trouve'?'border-left:3px solid #4a90d9':item.type==='poste'?'border-left:3px solid #2ecc71':'border-left:3px solid #e74c3c');
            var html = '<div style="display:flex;justify-content:space-between;margin-bottom:4px"><span style="color:#888">' + item.date + ' ' + item.heure + '</span></div>';
            if (item.type==='trouve') {
                html += '<div style="color:#4a90d9;font-weight:bold;margin-bottom:3px">' + (item.titre||'(sans titre)') + '</div>';
                if (item.auteur) html += '<div style="color:#aaa">Auteur : <span style="color:#eee">' + item.auteur + '</span></div>';
                if (item.topicUrl) html += '<a href="' + item.topicUrl + '" target="_blank" style="color:#4a90d9;text-decoration:none;font-size:10px">Voir →</a>';
            } else {
                html += '<div style="color:#aaa">Topic : <a href="' + (item.topicUrl||'#') + '" target="_blank" style="color:#2ecc71;text-decoration:none">' + (item.topicTitre||'?') + '</a></div>';
                if (item.message) html += '<div style="color:#eee;background:#111;padding:4px 6px;border-radius:3px;margin-top:3px">' + item.message.substring(0,100) + '</div>';
            }
            ligne.innerHTML = html;
            liste.appendChild(ligne);
        });
    }

    document.getElementById('os-sniper-historique-btn').addEventListener('click', function() { document.getElementById('os-vue-sniper').style.display='none'; document.getElementById('os-vue-historique').style.display='block'; afficherHistorique(); });
    document.getElementById('os-historique-retour').addEventListener('click', function() { document.getElementById('os-vue-historique').style.display='none'; document.getElementById('os-vue-sniper').style.display='block'; });
    document.getElementById('os-hist-tab-tracking').addEventListener('click', function() { ongletHistActif='tracking'; this.style.background='#4a90d9'; this.style.color='#fff'; this.style.border='none'; var p=document.getElementById('os-hist-tab-posting'); p.style.background='#2c3e50'; p.style.color='#aaa'; p.style.border='1px solid #2ecc71'; afficherHistorique(); });
    document.getElementById('os-hist-tab-posting').addEventListener('click', function() { ongletHistActif='posting'; this.style.background='#2ecc71'; this.style.color='#fff'; this.style.border='none'; var t=document.getElementById('os-hist-tab-tracking'); t.style.background='#2c3e50'; t.style.color='#aaa'; t.style.border='1px solid #4a90d9'; afficherHistorique(); });
    document.getElementById('os-historique-clear').addEventListener('click', function() { if (!confirm("Vider l'historique ?")) return; for (var d=0;d<7;d++){var date=new Date();date.setDate(date.getDate()-d);localStorage.removeItem('horlonche_historique_'+date.toLocaleDateString());} afficherHistorique(); });

    function restaurerSniper() {
        var raw = sessionStorage.getItem('horlonche_sniper_config');
        if (!raw) return;
        try {
            var config = JSON.parse(raw);
            if (!config.actif) return;
            document.getElementById('os-sniper-mot').value        = config.mot;
            document.getElementById('os-sniper-message').value    = config.message;
            document.getElementById('os-sniper-intervalle').value = config.intervalle;
            if (config.forums && Array.isArray(config.forums)) { sniperForums = config.forums; afficherForums(); }
            document.getElementById('os-sniper-start').click();
        } catch(e) {}
    }
    setTimeout(restaurerSniper, 300);

    document.getElementById('os-btn-sniper').addEventListener('click', function() { document.getElementById('os-vue-liste').style.display='none'; document.getElementById('os-vue-form').style.display='none'; document.getElementById('os-vue-sniper').style.display='block'; });
    document.getElementById('os-sniper-retour').addEventListener('click', function() { document.getElementById('os-vue-sniper').style.display='none'; document.getElementById('os-vue-liste').style.display='block'; });
    document.getElementById('os-sniper-start').addEventListener('click', function() {
        var mot = document.getElementById('os-sniper-mot').value.trim().toLowerCase().replace(/\s*,\s*/g,',');
        var message = document.getElementById('os-sniper-message').value.trim();
        var intervalle = parseInt(document.getElementById('os-sniper-intervalle').value) || 10;
        var scanUrl = sniperForums.length > 0 ? sniperForums : ['https://onche.org/forum/1/blabla-general'];
        if (!mot) { alert('Ecris un mot cle !'); return; }
        sniperActif = true;
        document.getElementById('os-sniper-start').style.display = 'none';
        document.getElementById('os-sniper-stop').style.display  = 'block';
        setSniperStatus('Actif — scan toutes les ' + intervalle + 's', '#2ecc71');
        sessionStorage.setItem('horlonche_sniper_config', JSON.stringify({actif:true,mot:mot,message:message,intervalle:intervalle,scanUrl:scanUrl,forums:sniperForums}));
        var forumsAScan = Array.isArray(scanUrl) ? scanUrl : [scanUrl];
        forumsAScan.forEach(function(url) { scannerTopics(mot, message, url); });
        sniperTimer = setInterval(function() { forumsAScan.forEach(function(url) { scannerTopics(mot,message,url); }); }, intervalle*1000);
    });
    document.getElementById('os-sniper-stop').addEventListener('click', function() {
        if (sniperTimer) clearInterval(sniperTimer);
        sniperTimer = null; sniperActif = false;
        document.getElementById('os-sniper-start').style.display = 'block';
        document.getElementById('os-sniper-stop').style.display  = 'none';
        setSniperStatus('Inactif', '#aaa');
        sessionStorage.removeItem('horlonche_sniper_config');
    });

    function scannerTopics(mot, message, scanUrl) {
        fetch(scanUrl||'https://onche.org/forum/1/blabla-general', {credentials:'include'}).then(function(r){return r.text();}).then(function(html) {
            var doc = new DOMParser().parseFromString(html,'text/html');
            doc.querySelectorAll('a.topic-subject, a[href*="/topic/"]').forEach(function(lien) {
                var titre = lien.textContent.trim().toLowerCase();
                var href  = lien.getAttribute('href');
                if (!href || href.indexOf('/topic/')===-1) return;
                var matchId = href.match(/\/topic\/(\d+)\//);
                if (!matchId) return;
                var topicId = matchId[1];
                if (sniperTopicsVus[topicId]) return;
                var mots = mot.split(',').map(function(m){return m.trim();}).filter(Boolean);
                if (!mots.some(function(m){return titre.indexOf(m)!==-1;})) return;
                sniperTopicsVus[topicId] = true; sauvegarderTopicsVus();
                ajouterLogSniper('🎯 ' + lien.textContent.trim().substring(0,40), 'trouve');
                ajouterHistorique('trouve',{titre:lien.textContent.trim(),auteur:'',forum:scanUrl,topicUrl:'https://onche.org'+href});
                posterDansTopicSniper(href, message, topicId);
            });
            setSniperStatus('Actif — dernier scan : ' + new Date().toLocaleTimeString(), '#2ecc71');
        }).catch(function() { setSniperStatus('Erreur de scan','#e74c3c'); });
    }

    function posterDansTopicSniper(topicHref, message, topicId) {
        var topicUrl = topicHref.indexOf('https://')===0 ? topicHref : 'https://onche.org' + topicHref.replace(/\/\d+$/,'/1');
        fetch(topicUrl,{credentials:'include'}).then(function(r){return r.text();}).then(function(html) {
            var match = html.match(/name="token"[^>]*value="([^"]+)"/) || html.match(/value="([^"]+)"[^>]*name="token"/) || html.match(/data-token="([^"]+)"/);
            var token = match ? match[1] : '';
            if (!token) { ajouterLogSniper('❌ Token introuvable #'+topicId,'erreur'); return; }
            var body = new URLSearchParams();
            body.append('message',message); body.append('poll_question',''); body.append('poll[]',''); body.append('token',token);
            return fetch(topicUrl.split('#')[0]+'#last',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body.toString(),credentials:'include'});
        }).then(function(r) {
            if (r&&(r.ok||r.redirected)) { ajouterLogSniper('✅ Poste #'+topicId,'poste'); ajouterHistorique('poste',{topicUrl:topicUrl,topicTitre:'Topic #'+topicId,message:message}); }
            else if (r) ajouterLogSniper('❌ Erreur '+r.status+' #'+topicId,'erreur');
        }).catch(function() { ajouterLogSniper('❌ Reseau #'+topicId,'erreur'); });
    }

    function setSniperStatus(t,c) { var el=document.getElementById('os-sniper-status'); if(el){el.textContent=t;el.style.color=c||'#aaa';} }
    function ajouterLogSniper(texte, type) {
        var log = document.getElementById('os-sniper-log');
        if (log) {
            var l=document.createElement('div');
            l.style.color=type==='poste'?'#2ecc71':type==='erreur'?'#e74c3c':type==='trouve'?'#4a90d9':'#888';
            l.textContent=new Date().toLocaleTimeString()+' — '+texte;
            log.insertBefore(l,log.firstChild);
            while(log.children.length>20) log.removeChild(log.lastChild);
        }
    }

    var messageCibleCapture = null;
    document.addEventListener('click', function(e) {
        var fleche = e.target.closest('[data-message-quote]');
        if (!fleche) return;
        var messageEl = fleche.closest('[data-id]');
        if (!messageEl) return;
        messageCibleCapture = { messageId: messageEl.getAttribute('data-id'), username: messageEl.getAttribute('data-username') || '' };
    }, true);

    var previewBox = document.createElement('div');
    previewBox.style.cssText = 'display:none;position:fixed;bottom:20px;right:310px;background:#1e2a3a;border:1px solid #4a90d9;border-radius:8px;padding:14px;z-index:99998;width:320px;max-height:280px;font-family:sans-serif;font-size:14px;color:#eee;box-shadow:0 4px 15px rgba(0,0,0,0.5);overflow-y:auto;word-break:break-word;white-space:pre-wrap;line-height:1.6';
    document.body.appendChild(previewBox);
    var previewOuvert = false;

    document.getElementById('os-btn-preview').addEventListener('click', function() {
        if (!previewOuvert) {
            majPreview();
            previewBox.style.display = 'block';
            this.textContent = '✕ Fermer la previsualisation'; this.style.color = '#4a90d9';
            previewOuvert = true;
            document.getElementById('os-message').addEventListener('input', majPreview);
            var chkSpear = document.getElementById('os-chk-spear');
        } else {
            previewBox.style.display = 'none';
            this.textContent = '👁 Previsualiser'; this.style.color = '#aaa';
            previewOuvert = false;
            document.getElementById('os-message').removeEventListener('input', majPreview);
        }
    });

    function majPreview() {
        var msg = document.getElementById('os-message').value || '';
        previewBox.innerHTML = '<div style="color:#eee;font-size:13px;white-space:pre-wrap">' + escHtml(msg || '(message vide)') + '</div>';
    }



    document.getElementById('os-btn-plus').addEventListener('click', function() { document.getElementById('os-vue-liste').style.display='none'; document.getElementById('os-vue-form').style.display='block'; document.getElementById('os-etape1').style.display='block'; document.getElementById('os-etape2').style.display='none'; vueActive='formulaire'; });
    document.getElementById('os-btn-annuler-form').addEventListener('click', retourListe);
    document.getElementById('os-btn-back').addEventListener('click', function() { document.getElementById('os-etape2').style.display='none'; document.getElementById('os-etape1').style.display='block'; modeChoisi=null; });

    function retourListe() {
        document.getElementById('os-vue-form').style.display  = 'none';
        document.getElementById('os-vue-liste').style.display = 'block';
        vueActive = 'liste'; modeChoisi = null;
        var btnSp = document.getElementById('os-chk-spear'); if(btnSp) btnSp.style.display='';
        if (previewOuvert) document.getElementById('os-btn-preview').click();
    }

    var modeBtns = panel.querySelectorAll('.os-mode-btn');
    for (var i = 0; i < modeBtns.length; i++) {
        modeBtns[i].addEventListener('click', function() {
            modeChoisi = this.getAttribute('data-mode');
            document.getElementById('os-etape1').style.display = 'none';
            document.getElementById('os-etape2').style.display = 'block';
            try { detecterInfosTopic(); } catch(e) {}
            var labels = {'topic':'Repondre dans le topic','citation':'Citer un message','nouveau':'Nouveau topic'};
            document.getElementById('os-label-mode').textContent = '→ ' + labels[modeChoisi];
            document.getElementById('os-instruction-citation').style.display = (modeChoisi==='citation') ? 'block' : 'none';
            document.getElementById('os-champ-titre').style.display           = (modeChoisi==='nouveau')  ? 'block' : 'none';
            var btnSp = document.getElementById('os-chk-spear');
            if(btnSp) btnSp.style.display = (modeChoisi==='citation') ? 'none' : '';
        });
    }

    document.getElementById('os-chk-jour').addEventListener('change', function() {
        var champ = document.getElementById('os-champ-jour');
        if (this.checked) { champ.style.display='block'; var auj=new Date(); document.getElementById('os-jour').value=auj.getFullYear()+'-'+pad(auj.getMonth()+1)+'-'+pad(auj.getDate()); }
        else { champ.style.display='none'; document.getElementById('os-jour').value=''; }
    });







    setTimeout(function() {
        var chk = document.getElementById('os-chk-troll');
        if (!chk) return;
        chk.addEventListener('change', function() {
            var info=document.getElementById('os-titre-troll-info'), titre=document.getElementById('os-titre');
            if (!info||!titre) return;
            if (this.checked) { info.style.display='block'; titre.style.borderColor='#f39c12'; titre.placeholder='Titre visible dans le 1er message...'; }
            else               { info.style.display='none';  titre.style.borderColor='#4a90d9'; titre.placeholder='Titre...'; }
        });
    }, 400);

    document.getElementById('os-btn-start').addEventListener('click', function() {
        var heureVal    = document.getElementById('os-heure').value;
        var heureVal    = document.getElementById('os-heure').value;
        var messageVal  = document.getElementById('os-message').value;



        var titreEl   = document.getElementById('os-titre');
        var titreVal  = titreEl ? titreEl.value.trim() : '';
        var jourVal   = document.getElementById('os-jour').value;
        var modeTroll = document.getElementById('os-chk-troll') && document.getElementById('os-chk-troll').checked;
        var vraiTitre = null;
        if (!heureVal) { alert('Choisis une heure !'); return; }
        if (modeChoisi === 'nouveau' && !titreVal && !modeTroll) { alert('Ecris un titre !'); return; }
        if (modeTroll) { vraiTitre=titreVal; if (!titreVal||titreVal.trim()==='') titreVal='\u200B'; }
        var parts = heureVal.split(':');
        var cible = new Date();
        if (jourVal) { var jp=jourVal.split('-'); cible.setFullYear(parseInt(jp[0]),parseInt(jp[1])-1,parseInt(jp[2])); }
        cible.setHours(parseInt(parts[0]),parseInt(parts[1]),parseInt(parts[2]||0),0);
        if (!jourVal && cible.getTime() <= Date.now()) cible.setDate(cible.getDate()+1);
        var labels = {'topic':'💬','citation':'↩','nouveau':'📝'};
        var labelJour = jourVal ? jourVal.split('-').reverse().join('/')+' ' : '';
        var labelAffiche = labels[modeChoisi]+' '+labelJour+heureVal;
        if (modeChoisi==='nouveau' && titreVal) labelAffiche += ' — '+titreVal.substring(0,20);
        if (modeChoisi==='citation' && messageCibleCapture) labelAffiche += ' (@'+(messageCibleCapture.username||'?')+')';
        var cibleInfoPourCetItem = (modeChoisi==='citation') ? messageCibleCapture : null;
        messageCibleCapture = null;
        if (modeChoisi==='citation' && !cibleInfoPourCetItem) { alert('Aucun message cible !'); return; }
        ajouterFile({ id:++idCounter, mode:modeChoisi, titre:titreVal, vraiTitre:vraiTitre||null, message:messageVal, tsEcheance:cible.getTime(), label:labelAffiche, heure:heureVal, pageUrl:window.location.href.split('#')[0], cibleInfo:cibleInfoPourCetItem });
        document.getElementById('os-heure').value = '';
        document.getElementById('os-message').value = '';
        if (titreEl) titreEl.value = '';
        document.getElementById('os-chk-jour').checked = false;
        document.getElementById('os-champ-jour').style.display = 'none';
        document.getElementById('os-jour').value = '';
        retourListe();
    });

    function ajouterFile(item) {
        fileAttente.push(item);
        afficherFile(); sauvegarderFile();
        item.timer = setInterval(function() {
            var resteMs = item.tsEcheance - Date.now();
            if (resteMs <= 0) { clearInterval(item.timer); executerItem(item); return; }
            var el = document.getElementById('os-countdown-'+item.id);
            if (el) { var s=Math.floor(resteMs/1000); el.textContent=pad(Math.floor(s/3600))+':'+pad(Math.floor((s%3600)/60))+':'+pad(s%60); }
        }, 200);
    }

    function supprimerFile(id) {
        for (var i=0;i<fileAttente.length;i++) { if (fileAttente[i].id===id) { clearInterval(fileAttente[i].timer); fileAttente.splice(i,1); break; } }
        afficherFile(); sauvegarderFile();
    }

    function afficherFile() {
        var liste=document.getElementById('os-file-liste'), vide=document.getElementById('os-file-vide');
        liste.style.cssText='max-height:220px;overflow-y:auto';
        if (fileAttente.length===0) { vide.style.display='block'; liste.innerHTML=''; return; }
        vide.style.display='none'; liste.innerHTML='';
        for (var i=0;i<fileAttente.length;i++) {
            (function(item) {
                var apercu = (item.message||'').replace(/[\u2800-\u28FF\u200B-\u200D\uFEFF\u00AD]/g,'').substring(0,40)+(item.message.length>40?'...':'');
                var ligne=document.createElement('div');
                ligne.style.cssText='padding:6px 8px;margin-bottom:5px;background:#0d1b2a;border:1px solid #2a3f55;border-radius:4px;font-size:12px';
                ligne.innerHTML='<div style="display:flex;align-items:center;justify-content:space-between"><span style="color:#eee;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:160px">'+item.label+'</span><div style="display:flex;align-items:center;gap:6px"><span id="os-countdown-'+item.id+'" style="color:#4a90d9;font-size:11px;font-weight:bold">--:--:--</span><button type="button" data-id="'+item.id+'" style="background:none;border:1px solid #c0392b;color:#c0392b;border-radius:3px;cursor:pointer;font-size:11px;padding:2px 5px">✕</button></div></div><div style="color:#8aa8c8;font-size:11px;margin-top:3px;font-style:italic;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+apercu+'</div>';
                ligne.querySelector('button').addEventListener('click', function() { supprimerFile(parseInt(this.getAttribute('data-id'))); });
                liste.appendChild(ligne);
            })(fileAttente[i]);
        }
    }

    function executerItem(item) {
        var el = document.getElementById('os-countdown-'+item.id);
        if (el) { el.textContent='Envoi...'; el.style.color='#f39c12'; }
        if (item.mode==='citation') posterAvecFetch(item,true);
        else if (item.mode==='nouveau') posterNouveauTopic(item);
        else posterAvecFetch(item,false);
    }

    function terminerItem(id, succes, msg) {
        var el = document.getElementById('os-countdown-'+id);
        if (el) { el.textContent=msg; el.style.color=succes?'#2ecc71':'#e74c3c'; }
        if (succes) demarrerCooldownAffichage();
        setTimeout(function() { for (var i=0;i<fileAttente.length;i++) { if(fileAttente[i].id===id){fileAttente.splice(i,1);break;} } afficherFile(); sauvegarderFile(); }, 3000);
    }

    var cooldownInterval = null;
    function demarrerCooldownAffichage() {
        var cdEl = document.getElementById('os-cooldown');
        if (!cdEl) return;
        if (cooldownInterval) clearInterval(cooldownInterval);
        cooldownInterval = setInterval(function() {
            var reste = Math.ceil((DELAI_MIN_POSTS-(Date.now()-dernierPost))/1000);
            if (reste<=0) { cdEl.innerHTML='&#9888; Delai min entre posts : <b>8s</b> — Cloudflare'; clearInterval(cooldownInterval); cooldownInterval=null; }
            else           cdEl.innerHTML='&#9888; Prochain post dans <b>'+reste+'s</b> — Cloudflare actif';
        }, 250);
    }

    function posterAvecFetch(item, estCitation) {
        if (postEnCours) { terminerItem(item.id,false,'Post en cours, attends !'); return; }
        if ((Date.now()-dernierPost) < DELAI_MIN_POSTS && dernierPost>0) { var att=Math.ceil((DELAI_MIN_POSTS-(Date.now()-dernierPost))/1000); terminerItem(item.id,false,'Trop rapide ! Attends '+att+'s'); return; }
        postEnCours = true;
        var failsafe = setTimeout(function(){if(postEnCours)postEnCours=false;},30000);
        var pageUrl  = normaliserUrlTopic((item.pageUrl||getUrlCible()).split('#')[0]);
        fetch(pageUrl,{credentials:'include'}).then(function(r){return r.text();}).then(function(html) {
            var match = html.match(/name="token"[^>]*value="([^"]+)"/) || html.match(/value="([^"]+)"[^>]*name="token"/) || html.match(/data-token="([^"]+)"/);
            var token = match ? match[1] : '';
            if (!token||token.length<10) { clearTimeout(failsafe); postEnCours=false; terminerItem(item.id,false,'Token invalide !'); return; }
            var body = new URLSearchParams();
            if (estCitation&&item.cibleInfo&&item.cibleInfo.messageId) { var mid=parseInt(item.cibleInfo.messageId); if(mid>0) body.append('answer',mid); }
            body.append('message',item.message); body.append('poll_question',''); body.append('poll[]',''); body.append('token',token);
            return fetch(pageUrl+'#last',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body.toString(),credentials:'include'});
        }).then(function(r) {
            clearTimeout(failsafe); postEnCours=false;
            if (r&&(r.ok||r.redirected)) { dernierPost=Date.now(); terminerItem(item.id,true,estCitation?'Citation postee !':'Poste !'); }
            else if (r) { if(r.status===429){terminerItem(item.id,false,'Rate limit 429 !');DELAI_MIN_POSTS=Math.min(DELAI_MIN_POSTS+5000,30000);}else terminerItem(item.id,false,'Erreur '+r.status); }
        }).catch(function(){clearTimeout(failsafe);postEnCours=false;terminerItem(item.id,false,'Erreur reseau !');});
    }

    function posterNouveauTopic(item) {
        if (postEnCours) { terminerItem(item.id,false,'Post en cours, attends !'); return; }
        if ((Date.now()-dernierPost)<DELAI_MIN_POSTS && dernierPost>0) { var att=Math.ceil((DELAI_MIN_POSTS-(Date.now()-dernierPost))/1000); terminerItem(item.id,false,'Trop rapide ! Attends '+att+'s'); return; }
        postEnCours = true;
        var pageUrl = normaliserUrlTopic((item.pageUrl||window.location.href).split('#')[0]);
        fetch(pageUrl,{credentials:'include'}).then(function(r){return r.text();}).then(function(html) {
            var match = html.match(/name="token"[^>]*value="([^"]+)"/) || html.match(/value="([^"]+)"[^>]*name="token"/) || html.match(/data-token="([^"]+)"/);
            var token = match ? match[1] : '';
            if (!token||token.length<10) { postEnCours=false; terminerItem(item.id,false,'Token introuvable !'); return; }
            var body = new URLSearchParams();
            body.append('title',item.titre); body.append('message',item.message); body.append('poll[]',''); body.append('token',token);
            return fetch(pageUrl+'#post',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body.toString(),credentials:'include'});
        }).then(function(r) {
            postEnCours=false;
            if (r&&(r.ok||r.redirected)) {
                dernierPost=Date.now();
                if (item.vraiTitre) { var mId=(r.url||'').match(/\/topic\/(\d+)\//); if(mId) setTimeout(function(){ posterPremierMessage('https://onche.org/topic/'+mId[1]+'/', item.vraiTitre); }, 2000); terminerItem(item.id,true,'Topic troll cree !'); }
                else terminerItem(item.id,true,'Topic cree !');
            } else if (r) terminerItem(item.id,false,'Erreur '+r.status);
        }).catch(function(){postEnCours=false;terminerItem(item.id,false,'Erreur reseau !');});
    }

    function posterPremierMessage(topicUrl, texte) {
        fetch(topicUrl,{credentials:'include'}).then(function(r){return r.text();}).then(function(html) {
            var match = html.match(/name="token"[^>]*value="([^"]+)"/) || html.match(/value="([^"]+)"[^>]*name="token"/) || html.match(/data-token="([^"]+)"/);
            var token = match ? match[1] : '';
            if (!token||token.length<10) return;
            var body=new URLSearchParams(); body.append('message',texte); body.append('poll_question',''); body.append('poll[]',''); body.append('token',token);
            fetch(topicUrl+'#last',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body.toString(),credentials:'include'});
        }).catch(function(){});
    }



    // =========================================================
    //  WATCHDOG
    // =========================================================

    var watchdogTimer = null, watchdogActif = false;
    var watchdogPseudos = [];
    var watchdogForums  = ['https://onche.org/forum/1/blabla-general'];
    var watchdogMessagesVus = {};
    var watchdogTopicsDetectes = {}; // topicId -> {url, titre, pseudo}
    try { var rawWD = sessionStorage.getItem('horlonche_watchdog_vus'); if (rawWD) watchdogMessagesVus = JSON.parse(rawWD); } catch(e) {}

    function sauvegarderWatchdogVus() {
        try {
            var keys = Object.keys(watchdogMessagesVus);
            if (keys.length > 500) { var n={}; keys.slice(-500).forEach(function(k){n[k]=true;}); watchdogMessagesVus=n; }
            sessionStorage.setItem('horlonche_watchdog_vus', JSON.stringify(watchdogMessagesVus));
        } catch(e) {}
    }

    function ajouterHistoriqueWatchdog(data) {
        var today = new Date().toLocaleDateString();
        var key   = 'horlonche_watchdog_hist_' + today;
        var liste = [];
        try { var r = localStorage.getItem(key); if (r) liste = JSON.parse(r); } catch(e) {}
        liste.unshift(Object.assign({ heure: new Date().toLocaleTimeString() }, data));
        if (liste.length > 200) liste = liste.slice(0, 200);
        try { localStorage.setItem(key, JSON.stringify(liste)); } catch(e) {}
    }

    function chargerHistoriqueWatchdog() {
        var entrees = [];
        for (var d = 0; d < 7; d++) {
            var date = new Date(); date.setDate(date.getDate() - d);
            var raw = localStorage.getItem('horlonche_watchdog_hist_' + date.toLocaleDateString());
            try { if (raw) { var items = JSON.parse(raw); items.forEach(function(i){i.date=date.toLocaleDateString();}); entrees = entrees.concat(items); } } catch(e) {}
        }
        return entrees;
    }

    function afficherHistoriqueWatchdog() {
        var liste = document.getElementById('os-wd-hist-liste');
        if (!liste) return;
        liste.innerHTML = '';
        var entrees = chargerHistoriqueWatchdog();
        if (entrees.length === 0) { liste.innerHTML = '<div style="color:#aaa;text-align:center;padding:20px;font-size:12px">Aucune detection</div>'; return; }
        entrees.forEach(function(item) {
            var ligne = document.createElement('div');
            ligne.style.cssText = 'padding:7px 8px;margin-bottom:5px;background:#0d1b2a;border-radius:4px;font-size:11px;' + (item.type==='erreur'?'border-left:3px solid #e74c3c':'border-left:3px solid #9b59b6');
            var html = '<div style="display:flex;justify-content:space-between;margin-bottom:4px"><span style="color:#888">' + (item.date||'') + ' ' + item.heure + '</span><span style="color:#e67e22;font-weight:bold">@' + escHtml(item.pseudo||'') + '</span></div>';
            html += '<div style="color:#aaa;margin-bottom:2px">' + escHtml(item.topicTitre||'') + '</div>';
            if (item.extrait) html += '<div style="color:#eee;background:#111;padding:4px 6px;border-radius:3px;margin-top:3px">' + escHtml(item.extrait) + '</div>';
            if (item.topicUrl) html += '<a href="' + escHtml(item.topicUrl) + '" target="_blank" style="color:#9b59b6;font-size:10px;text-decoration:none">Voir →</a>';
            if (item.erreur) html += '<div style="color:#e74c3c">' + escHtml(item.erreur) + '</div>';
            ligne.innerHTML = html;
            liste.appendChild(ligne);
        });
    }

    function afficherWatchdogPseudos() {
        var el = document.getElementById('os-wd-pseudos-liste');
        if (!el) return;
        el.innerHTML = '';
        watchdogPseudos.forEach(function(p, idx) {
            var ligne = document.createElement('div');
            ligne.style.cssText = 'display:flex;align-items:center;gap:4px;margin-bottom:4px;background:#0d1b2a;border:1px solid #3a2000;border-radius:4px;padding:3px 6px';
            ligne.innerHTML = '<span style="flex:1;color:#eee;font-size:11px">' + escHtml(p) + '</span><button type="button" data-idx="' + idx + '" style="background:none;border:none;color:#c0392b;cursor:pointer;font-size:13px;padding:0 2px">✕</button>';
            ligne.querySelector('button').addEventListener('click', function() { watchdogPseudos.splice(parseInt(this.getAttribute('data-idx')),1); afficherWatchdogPseudos(); });
            el.appendChild(ligne);
        });
    }

    function afficherWatchdogForums() {
        var el = document.getElementById('os-wd-forums-liste');
        if (!el) return;
        el.innerHTML = '';
        watchdogForums.forEach(function(url, idx) {
            var ligne = document.createElement('div');
            ligne.style.cssText = 'display:flex;align-items:center;gap:4px;margin-bottom:4px;background:#0d1b2a;border:1px solid #3a2000;border-radius:4px;padding:3px 6px';
            var nom = url.replace('https://onche.org/forum/','').replace(/\/.*/,'');
            nom = ({1:'Blabla General',4:'Goulag',7:'Finance',8:'Jeux Video',9:'Autonomie',13:'Videoclub'})[nom] || url.split('/').pop();
            ligne.innerHTML = '<span style="flex:1;color:#eee;font-size:11px">' + nom + '</span><button type="button" data-idx="' + idx + '" style="background:none;border:none;color:#c0392b;cursor:pointer;font-size:13px;padding:0 2px">✕</button>';
            ligne.querySelector('button').addEventListener('click', function() { watchdogForums.splice(parseInt(this.getAttribute('data-idx')),1); afficherWatchdogForums(); });
            el.appendChild(ligne);
        });
    }

    function scannerWatchdog() {
        if (watchdogPseudos.length === 0) return;
        watchdogForums.forEach(function(forumUrl) {
            fetch(forumUrl, { credentials: 'include' }).then(function(r){return r.text();}).then(function(html) {
                var doc = new DOMParser().parseFromString(html, 'text/html');
                var topicEls = doc.querySelectorAll('.topic');
                topicEls.forEach(function(topicEl) {
                    var pseudoEl = topicEl.querySelector('.topic-username');
                    if (!pseudoEl) return;
                    var dernierPosteur = pseudoEl.textContent.trim().toLowerCase();
                    var surveille = watchdogPseudos.some(function(p){ return dernierPosteur === p.toLowerCase(); });
                    if (!surveille) return;
                    var lienEl = topicEl.querySelector('a.topic-subject');
                    if (!lienEl) return;
                    var href   = lienEl.getAttribute('href');
                    var matchId = href ? href.match(/\/topic\/(\d+)/) : null;
                    if (!matchId) return;
                    var topicId = matchId[1];
                    // Titre : premier span sans classe topic-nb
                    var titre = '';
                    var spans = lienEl.querySelectorAll('span');
                    for (var si=0; si<spans.length; si++) {
                        if (!spans[si].className || spans[si].className.indexOf('topic-nb')===-1) {
                            titre = spans[si].textContent.trim();
                            if (titre) break;
                        }
                    }
                    if (!titre) titre = lienEl.firstChild && lienEl.firstChild.textContent ? lienEl.firstChild.textContent.trim() : '';
                    if (!titre) titre = 'Topic #' + topicId;
                    var topicUrl = href.indexOf('https://') === 0 ? href : 'https://onche.org' + href;
                    topicUrl = topicUrl.split('#')[0];
                    if (!/\/\d+$/.test(topicUrl)) topicUrl = topicUrl + '/1';
                    entrerDansTopicWatchdog(topicId, topicUrl, titre);
                });
                setWatchdogStatus('Actif — dernier scan : ' + new Date().toLocaleTimeString(), '#2ecc71');
            }).catch(function(){ setWatchdogStatus('Erreur de scan', '#e74c3c'); });
        });
    }

    function entrerDansTopicWatchdog(topicId, topicUrl, topicTitre) {
        var baseUrl = topicUrl.split('#')[0];
        if (baseUrl.indexOf('http') !== 0) baseUrl = 'https://onche.org' + baseUrl;
        baseUrl = baseUrl.replace(/\/\d+$/, '');
        var pageUrl = baseUrl + '/1';
        fetch(pageUrl, { credentials: 'include' }).then(function(r){return r.text();}).then(function(html) {
            var doc = new DOMParser().parseFromString(html, 'text/html');
            var lastPageBtn = doc.querySelector('a.mdi-chevron-right:not(.disabled)');
            if (lastPageBtn) {
                var lastHref  = lastPageBtn.getAttribute('href') || '';
                var lastMatch = lastHref.match(/\/topic\/\d+\/[^/]+\/(\d+)/);
                if (lastMatch) {
                    var lastUrl = baseUrl + '/' + parseInt(lastMatch[1]);
                    fetch(lastUrl, { credentials: 'include' }).then(function(r2){return r2.text();}).then(function(html2){ parseMessagesWatchdog(html2, topicId, topicUrl, topicTitre, baseUrl); }).catch(function(){});
                    return;
                }
            }
            parseMessagesWatchdog(html, topicId, topicUrl, topicTitre, baseUrl);
        }).catch(function(){});
    }

    function parseMessagesWatchdog(html, topicId, topicUrl, topicTitre, baseUrl) {
        var doc = new DOMParser().parseFromString(html, 'text/html');
        var tous = doc.querySelectorAll('.message[data-id][data-username]');
        var messages = Array.prototype.filter.call(tous, function(el){ return !el.classList.contains('answer'); });
        messages.forEach(function(msgEl) {
            var msgId    = msgEl.getAttribute('data-id');
            var username = (msgEl.getAttribute('data-username') || '').toLowerCase();
            var surveille = watchdogPseudos.some(function(p){ return username === p.toLowerCase(); });
            if (!surveille) return;
            var cle = username + '_msg_' + msgId;
            if (watchdogMessagesVus[cle]) return;
            watchdogMessagesVus[cle] = true; sauvegarderWatchdogVus();
            var contenuEl = msgEl.querySelector('.message-content');
            var extrait   = contenuEl ? contenuEl.textContent.trim().substring(0, 80) : '';
            var topicBase      = topicUrl.indexOf('https://') === 0 ? topicUrl : 'https://onche.org' + topicUrl;
            var topicUrlPropre = topicBase.split('#')[0] + '#message_' + msgId;
            setWatchdogStatus('🐕 @' + username + ' detecte dans ' + topicTitre.substring(0, 20), '#9b59b6');
            ajouterLogWatchdog('🐕 @' + username + ' — ' + topicTitre.substring(0,30), '#9b59b6');
            // Stocker le topic pour la liste cliquable
            if (!watchdogTopicsDetectes[topicId]) {
                watchdogTopicsDetectes[topicId] = { url: topicBase.split('#')[0] + '/1', titre: topicTitre, pseudo: username };
                afficherTopicsDetectes();
            }
            ajouterHistoriqueWatchdog({ type:'detection', pseudo:username, topicUrl:topicUrlPropre, topicTitre:topicTitre, extrait:extrait });
            // Mise a jour de l'historique commun aussi
            ajouterHistorique('trouve', {titre:topicTitre, auteur:username, topicUrl:topicUrlPropre});
            var reponse = document.getElementById('os-wd-message') ? document.getElementById('os-wd-message').value.trim() : '';
            if (reponse) posterReponseWatchdog(topicUrlPropre, msgId, topicTitre, username, reponse);
        });
    }

    function posterReponseWatchdog(topicUrl, msgId, topicTitre, pseudo, reponse) {
        var baseUrl   = topicUrl.indexOf('https://') === 0 ? topicUrl : 'https://onche.org' + topicUrl;
        var topicBase = baseUrl.split('#')[0].replace(/\/\d+$/, '');
        var pageUrl   = topicBase + '/1';
        fetch(pageUrl, { credentials: 'include' }).then(function(r){return r.text();}).then(function(html) {
            var match = html.match(/name="token"[^>]*value="([^"]+)"/) || html.match(/value="([^"]+)"[^>]*name="token"/) || html.match(/data-token="([^"]+)"/);
            var token = match ? match[1] : '';
            if (!token || token.length < 10) { ajouterHistoriqueWatchdog({type:'erreur',pseudo:pseudo,topicUrl:baseUrl,topicTitre:topicTitre,erreur:'Token introuvable'}); return; }
            var body = new URLSearchParams();
            body.append('message', reponse); body.append('poll_question',''); body.append('poll[]',''); body.append('token', token);
            return fetch(topicBase + '/1#last', {method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'}, body:body.toString(), credentials:'include'});
        }).then(function(r) {
            if (r&&(r.ok||r.redirected)) { ajouterLogWatchdog('✅ Reponse postee', '#2ecc71'); ajouterHistorique('poste',{topicUrl:topicUrl,topicTitre:topicTitre,message:reponse}); }
            else if (r) ajouterLogWatchdog('❌ Erreur ' + r.status, '#e74c3c');
        }).catch(function(){ ajouterLogWatchdog('❌ Erreur reseau', '#e74c3c'); });
    }

    function setWatchdogStatus(t, c) { var el=document.getElementById('os-wd-status'); if(el){el.textContent=t;el.style.color=c||'#aaa';} }

    function afficherTopicsDetectes() {
        var liste = document.getElementById('os-wd-topics-liste');
        if (!liste) return;
        var ids = Object.keys(watchdogTopicsDetectes);
        if (ids.length === 0) {
            liste.innerHTML = '<span style="color:#666;font-size:10px">Aucun topic trouve pour ces pseudos.</span>';
            return;
        }
        liste.innerHTML = '';
        ids.forEach(function(id) {
            var t = watchdogTopicsDetectes[id];
            var ligne = document.createElement('div');
            ligne.style.cssText = 'display:flex;align-items:center;gap:5px;margin-bottom:4px;padding:3px 5px;background:#1a2a3a;border-radius:3px';
            var cbId = 'os-wd-cb-' + id;
            ligne.innerHTML = '<input type="checkbox" id="' + cbId + '" data-id="' + id + '" checked style="cursor:pointer;flex-shrink:0">'
                + '<span style="color:#e67e22;font-size:10px;flex-shrink:0">@' + escHtml(t.pseudo) + '</span>'
                + '<a href="' + escHtml(t.url) + '" target="_blank" style="flex:1;color:#eee;font-size:10px;text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis" title="' + escHtml(t.titre) + '">' + escHtml(t.titre.substring(0,35)) + '</a>';
            liste.appendChild(ligne);
        });
    }

    function scannerTopicsManuellement() {
        if (watchdogPseudos.length === 0) { alert('Ajoute au moins un pseudo a surveiller !'); return; }
        var btn = document.getElementById('os-wd-scan-now');
        if (btn) { btn.textContent = '⏳ Scan...'; btn.disabled = true; }
        watchdogTopicsDetectes = {};
        var liste = document.getElementById('os-wd-topics-liste');
        if (liste) liste.innerHTML = '<span style="color:#e67e22;font-size:10px">Scan en cours...</span>';
        var forums = watchdogForums.length > 0 ? watchdogForums : ['https://onche.org/forum/1/blabla-general'];
        var total = forums.length, done = 0;
        forums.forEach(function(forumUrl) {
            fetch(forumUrl, { credentials: 'include' }).then(function(r){return r.text();}).then(function(html) {
                var doc = new DOMParser().parseFromString(html, 'text/html');
                doc.querySelectorAll('.topic').forEach(function(topicEl) {
                    var pseudoEl = topicEl.querySelector('.topic-username');
                    if (!pseudoEl) return;
                    var dernierPosteur = pseudoEl.textContent.trim().toLowerCase();
                    var surveille = watchdogPseudos.some(function(p){ return dernierPosteur === p.toLowerCase(); });
                    if (!surveille) return;
                    var lienEl = topicEl.querySelector('a.topic-subject');
                    if (!lienEl) return;
                    var href = lienEl.getAttribute('href');
                    var matchId = href ? href.match(/\/topic\/(\d+)/) : null;
                    if (!matchId) return;
                    var topicId = matchId[1];
                    // Titre : premier span direct, sans topic-nb
                    var titre = '';
                    var spans = lienEl.querySelectorAll('span');
                    for (var si=0; si<spans.length; si++) {
                        if (!spans[si].className || spans[si].className.indexOf('topic-nb')===-1) {
                            titre = spans[si].textContent.trim();
                            if (titre) break;
                        }
                    }
                    if (!titre) titre = lienEl.firstChild && lienEl.firstChild.textContent ? lienEl.firstChild.textContent.trim() : '';
                    if (!titre) titre = 'Topic #' + topicId;
                    var topicUrl = (href.indexOf('https://')===0?href:'https://onche.org'+href).split('#')[0];
                    if (!/\/\d+$/.test(topicUrl)) topicUrl += '/1';
                    watchdogTopicsDetectes[topicId] = { url: topicUrl, titre: titre, pseudo: dernierPosteur };
                });
            }).catch(function(){}).then(function() {
                done++;
                if (done >= total) {
                    // Ouvrir la vue topics et afficher les resultats
                    var vConfig = document.getElementById('os-wd-vue-config');
                    var vTopics = document.getElementById('os-wd-vue-topics');
                    if (vConfig) vConfig.style.display = 'none';
                    if (vTopics) vTopics.style.display = 'block';
                    afficherTopicsDetectes();
                    if (btn) { btn.textContent = '🔍 Scan'; btn.disabled = false; }
                }
            });
        });
    }

    var wdPosterEnCours = false;

    function posterSurListe(ids, btnId) {
        var msg = document.getElementById('os-wd-message') ? document.getElementById('os-wd-message').value.trim() : '';
        if (!msg) { alert('Ecris un message dans le champ Message auto !'); return; }
        if (wdPosterEnCours) { alert('Envoi en cours...'); return; }
        if (ids.length === 0) { alert('Aucun topic selectionne !'); return; }
        wdPosterEnCours = true;
        var i = 0;
        var btn = document.getElementById(btnId);
        var labelOriginal = btn ? btn.textContent : '';
        function envoyerProchain() {
            if (i >= ids.length) {
                wdPosterEnCours = false;
                if (btn) { btn.textContent = labelOriginal; btn.disabled = false; }
                ajouterLogWatchdog('✅ Envoi termine sur ' + ids.length + ' topics', '#2ecc71');
                return;
            }
            var t = watchdogTopicsDetectes[ids[i]];
            if (!t) { i++; envoyerProchain(); return; }
            if (btn) btn.textContent = 'Envoi ' + (i+1) + '/' + ids.length + '...';
            posterReponseWatchdog(t.url, null, t.titre, t.pseudo, msg);
            ajouterLogWatchdog('📤 ' + (i+1) + '/' + ids.length + ' — ' + t.titre.substring(0,25), '#2ecc71');
            i++;
            if (i < ids.length) setTimeout(envoyerProchain, 8500);
            else {
                wdPosterEnCours = false;
                if (btn) { btn.textContent = labelOriginal; btn.disabled = false; }
                ajouterLogWatchdog('✅ Envoi termine sur ' + ids.length + ' topics', '#2ecc71');
            }
        }
        if (btn) btn.disabled = true;
        envoyerProchain();
    }

    function posterSurSelection() {
        var ids = [];
        document.querySelectorAll('#os-wd-topics-liste input[type="checkbox"]').forEach(function(cb) {
            if (cb.checked) ids.push(cb.getAttribute('data-id'));
        });
        posterSurListe(ids, 'os-wd-poster-selection');
    }

    function posterSurTous() {
        var ids = Object.keys(watchdogTopicsDetectes);
        posterSurListe(ids, 'os-wd-poster-tous');
    }
    function ajouterLogWatchdog(texte, couleur) {
        var log = document.getElementById('os-wd-log');
        if (log) {
            var l = document.createElement('div');
            l.style.color = couleur || '#888';
            l.textContent = new Date().toLocaleTimeString() + ' — ' + texte;
            log.insertBefore(l, log.firstChild);
            while (log.children.length > 20) log.removeChild(log.lastChild);
        }
    }

    // Listeners Watchdog
    setTimeout(function() {
        var btnWD = document.getElementById('os-btn-watchdog');
        if (btnWD) btnWD.addEventListener('click', function() {
            ['os-vue-liste','os-vue-form','os-vue-sniper','os-vue-historique'].forEach(function(id){var el=document.getElementById(id);if(el)el.style.display='none';});
            var vw = document.getElementById('os-vue-watchdog'); if(vw) vw.style.display='block';
            afficherWatchdogPseudos(); afficherWatchdogForums();
        });
        var btnWDRetour = document.getElementById('os-wd-retour');
        if (btnWDRetour) btnWDRetour.addEventListener('click', function() {
            var vw=document.getElementById('os-vue-watchdog');if(vw)vw.style.display='none';
            var vl=document.getElementById('os-vue-liste');if(vl)vl.style.display='block';
        });
        var btnPseudoAdd = document.getElementById('os-wd-pseudo-add');
        if (btnPseudoAdd) btnPseudoAdd.addEventListener('click', function() {
            var inp = document.getElementById('os-wd-pseudo-input');
            var val = inp ? inp.value.trim() : '';
            if (!val) return;
            if (watchdogPseudos.indexOf(val) !== -1) { alert('Pseudo deja dans la liste'); return; }
            watchdogPseudos.push(val); inp.value = ''; afficherWatchdogPseudos();
        });
        var btnForumAdd = document.getElementById('os-wd-forum-add');
        if (btnForumAdd) btnForumAdd.addEventListener('click', function() {
            var inp = document.getElementById('os-wd-forum-input');
            var url = inp ? inp.value.trim() : '';
            if (!url) return;
            if (url.indexOf('onche.org/forum/') === -1) { alert('URL invalide'); return; }
            if (watchdogForums.indexOf(url) !== -1) { alert('Forum deja dans la liste'); return; }
            watchdogForums.push(url); inp.value = ''; afficherWatchdogForums();
        });
        var btnWDStart = document.getElementById('os-wd-start');
        if (btnWDStart) btnWDStart.addEventListener('click', function() {
            if (watchdogPseudos.length === 0) { alert('Ajoute au moins un pseudo !'); return; }
            var intervalle = parseInt(document.getElementById('os-wd-intervalle').value) || 15;
            watchdogActif = true;
            document.getElementById('os-wd-start').style.display = 'none';
            document.getElementById('os-wd-stop').style.display  = 'block';
            setWatchdogStatus('Actif — scan toutes les ' + intervalle + 's', '#2ecc71');
            scannerWatchdog();
            watchdogTimer = setInterval(scannerWatchdog, intervalle * 1000);
        });
        var btnWDHist = document.getElementById('os-wd-historique-btn');
        if (btnWDHist) btnWDHist.addEventListener('click', function() {
            document.getElementById('os-vue-watchdog').style.display      = 'none';
            document.getElementById('os-vue-wd-historique').style.display = 'block';
            afficherHistoriqueWatchdog();
        });
        var btnWDHistRetour = document.getElementById('os-wd-hist-retour');
        if (btnWDHistRetour) btnWDHistRetour.addEventListener('click', function() {
            document.getElementById('os-vue-wd-historique').style.display = 'none';
            document.getElementById('os-vue-watchdog').style.display      = 'block';
        });
        var btnWDHistClear = document.getElementById('os-wd-hist-clear');
        if (btnWDHistClear) btnWDHistClear.addEventListener('click', function() {
            if (!confirm("Vider l'historique Watchdog ?")) return;
            for (var d=0;d<7;d++){var date=new Date();date.setDate(date.getDate()-d);localStorage.removeItem('horlonche_watchdog_hist_'+date.toLocaleDateString());}
            afficherHistoriqueWatchdog();
        });
        var btnScanNow = document.getElementById('os-wd-scan-now');
        if (btnScanNow) btnScanNow.addEventListener('click', scannerTopicsManuellement);

        var btnVoirTopics = document.getElementById('os-wd-voir-topics');
        if (btnVoirTopics) btnVoirTopics.addEventListener('click', function() {
            document.getElementById('os-wd-vue-config').style.display  = 'none';
            document.getElementById('os-wd-vue-topics').style.display  = 'block';
            afficherTopicsDetectes();
        });

        var btnTopicsRetour = document.getElementById('os-wd-topics-retour');
        if (btnTopicsRetour) btnTopicsRetour.addEventListener('click', function() {
            document.getElementById('os-wd-vue-topics').style.display = 'none';
            document.getElementById('os-wd-vue-config').style.display = 'block';
        });

        var btnPosterSelection = document.getElementById('os-wd-poster-selection');
        if (btnPosterSelection) btnPosterSelection.addEventListener('click', posterSurSelection);

        var btnPosterTous = document.getElementById('os-wd-poster-tous');
        if (btnPosterTous) btnPosterTous.addEventListener('click', posterSurTous);

        var btnWDStop = document.getElementById('os-wd-stop');
        if (btnWDStop) btnWDStop.addEventListener('click', function() {
            if (watchdogTimer) clearInterval(watchdogTimer);
            watchdogTimer = null; watchdogActif = false;
            watchdogTopicsDetectes = {};
            afficherTopicsDetectes();
            document.getElementById('os-wd-start').style.display = 'block';
            document.getElementById('os-wd-stop').style.display  = 'none';
            setWatchdogStatus('Inactif', '#aaa');
        });
    }, 500);

    function pad(n)    { return n < 10 ? '0'+n : ''+n; }
    function escHtml(s){ return s?String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'):''; }

})();