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;'):''; }

})();