Greasy Fork is available in English.
Planificateur de posts, sniper de topics et historique pour Onche.org
// ==UserScript==
// @name Horlonche
// @namespace http://tampermonkey.net/
// @version 11.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;
// File d'attente : liste de { id, mode, titre, message, tsEcheance, timer, label }
var fileAttente = [];
var idCounter = 0;
// =========================================================
// Sauvegarde / restauration de la file via sessionStorage
// =========================================================
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
};
}).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;
// =========================================================
// Panel principal
// =========================================================
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 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 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 programmé.<br>Clique sur <b>+</b> pour en ajouter un.</div>'
+ '<div id="os-file-liste"></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 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">📝 Créer un nouveau topic</button>'
: '<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">💬 Répondre dans le topic</button>'
+ '<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 / répondre à un message</button>'
)
+ '<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 flèche du message AVANT de cliquer "Ajouter"</div>'
+ '<div id="os-champ-titre" style="display:none">'
+ '<label style="display:block;margin-bottom:4px">Titre du topic</label>'
+ '<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:10px;box-sizing:border-box">'
+ '</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 id="os-chk-jour" type="checkbox" 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 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">👁 Prévisualiser</button>'
+ '<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 à la file</button>'
+ '<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 à 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 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 clés (séparés par des virgules)</label>'
+ '<input id="os-sniper-mot" type="text" placeholder="ex: post, 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 à 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 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 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">Désactiver</button>'
+ '<div style="display:flex;gap:4px">'
+ '<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 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-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 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 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 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 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 id="os-vue-aspirateur" style="display:none">'
+ '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px">'
+ '<span style="font-weight:bold;font-size:13px;color:#e67e22">🔍 Aspirateur de profil</span>'
+ '</div>'
+ '<div style="display:flex;gap:4px;margin-bottom:10px">'
+ '<input id="os-asp-pseudo" type="text" placeholder="Pseudo cible..." style="flex:1;padding:6px;background:#0d1b2a;color:#eee;border:1px solid #e67e22;border-radius:4px;font-size:13px;box-sizing:border-box">'
+ '<button id="os-asp-go" style="padding:6px 10px;background:#e67e22;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px">Go</button>'
+ '</div>'
+ '<div id="os-asp-status" style="font-size:11px;color:#aaa;text-align:center;margin-bottom:6px"></div>'
+ '<button id="os-asp-retour" style="width:100%;padding:5px;background:#2c3e50;color:#aaa;border:1px solid #555;border-radius:4px;cursor:pointer;font-size:12px">← Retour</button>'
+ '</div>'
+ '<div id="os-vue-aspirateur-fiche" style="display:none">'
+ '<div id="os-asp-fiche-contenu"></div>'
+ '<button id="os-asp-fiche-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();
// =========================================================
// Sniper de topic
// =========================================================
var sniperTimer = null;
var sniperActif = false;
var sniperTopicsVus = {};
// Charge les topics deja vus depuis sessionStorage
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) {}
}
// =========================================================
// Liste des forums a scanner
// =========================================================
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;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">' + nom + '</span>'
+ '<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);
});
}
// Initialiser avec Blabla General et afficher
setTimeout(function() {
afficherForums();
document.getElementById('os-sniper-url-add').addEventListener('click', function() {
var input = document.getElementById('os-sniper-url-input');
var url = input.value.trim();
if (!url) return;
if (url.indexOf('onche.org/forum/') === -1) { alert('URL invalide - doit etre un forum Onche'); return; }
if (sniperForums.indexOf(url) !== -1) { alert('Forum deja dans la liste'); return; }
sniperForums.push(url);
input.value = '';
afficherForums();
});
// Aussi ajouter en appuyant Entree
document.getElementById('os-sniper-url-input').addEventListener('keydown', function(e) {
if (e.key === 'Enter') document.getElementById('os-sniper-url-add').click();
});
}, 400);
// =========================================================
// Historique du sniper (localStorage = survit a la fermeture)
// =========================================================
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 key = date.toLocaleDateString();
var raw = localStorage.getItem('horlonche_historique_' + key);
try {
if (raw) {
var items = JSON.parse(raw);
items.forEach(function(item) { item.date = key; });
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');
});
// Mettre a jour les compteurs
var nbTracking = entrees.filter(function(e) { return e.type === 'trouve'; }).length;
var nbPosting = 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 (' + nbTracking + ')';
if (btnP) btnP.textContent = 'Posting (' + nbPosting + ')';
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>'
+ (item.type === 'erreur' ? '<span style="color:#e74c3c;font-size:10px">ERREUR</span>' : '')
+ '</div>';
if (item.type === 'trouve') {
// Tracking
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.forum) html += '<div style="color:#aaa">Forum : <span style="color:#eee">' + item.forum + '</span></div>';
if (item.topicUrl) html += '<a href="' + item.topicUrl + '" target="_blank" style="color:#4a90d9;text-decoration:none;font-size:10px;display:inline-block;margin-top:3px">Voir le topic →</a>';
} else {
// Posting
html += '<div style="color:#aaa;margin-bottom:3px">Topic : <a href="' + (item.topicUrl || '#') + '" target="_blank" style="color:#2ecc71;text-decoration:none">' + (item.topicTitre || item.topicUrl || '?') + '</a></div>';
if (item.message) html += '<div style="color:#eee;background:#111;padding:4px 6px;border-radius:3px;margin-top:3px;word-break:break-word">' + item.message.substring(0, 100) + (item.message.length > 100 ? '...' : '') + '</div>';
if (item.type === 'erreur' && item.erreur) html += '<div style="color:#e74c3c;margin-top:3px">' + item.erreur + '</div>';
}
ligne.innerHTML = html;
liste.appendChild(ligne);
});
}
// Bouton historique
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 tout 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();
});
// Restaurer le sniper apres refresh
function restaurerSniper() {
var raw = sessionStorage.getItem('horlonche_sniper_config');
if (!raw) return;
try {
var config = JSON.parse(raw);
if (!config.actif) return;
// Remplir les champs
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(); }
// Relancer le sniper automatiquement
document.getElementById('os-sniper-start').click();
ajouterLogSniper('Sniper relancé après refresh.', 'info');
} catch(e) {}
}
// Lancer apres que le DOM est pret
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 clé !'); return; }
if (!message) { alert('Ecris un message !'); 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');
ajouterLogSniper('Sniper activé : "' + mot + '"', 'info');
// Sauvegarder l'état pour survie au refresh
sessionStorage.setItem('horlonche_sniper_config', JSON.stringify({
actif: true, mot: mot, message: message, intervalle: intervalle, scanUrl: scanUrl, forums: sniperForums
}));
// Scan immediat puis toutes les X secondes
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');
ajouterLogSniper('Sniper désactivé.', 'info');
sessionStorage.removeItem('horlonche_sniper_config');
});
function scannerTopics(mot, message, scanUrl) {
var forumUrl = scanUrl || 'https://onche.org/forum/1/blabla-general';
setSniperStatus('Scan en cours...', '#f39c12');
fetch(forumUrl, { credentials: 'include' })
.then(function(r) { return r.text(); })
.then(function(html) {
// Extrait tous les liens de topics avec leur titre
var parser = new DOMParser();
var doc = parser.parseFromString(html, 'text/html');
var liens = doc.querySelectorAll('a.topic-subject, a[href*="/topic/"]');
var trouves = 0;
liens.forEach(function(lien) {
var titre = lien.textContent.trim().toLowerCase();
var href = lien.getAttribute('href');
if (!href || href.indexOf('/topic/') === -1) return;
// Extrait l'ID du topic depuis l'URL
var matchId = href.match(/\/topic\/(\d+)\//);
if (!matchId) return;
var topicId = matchId[1];
// Deja traite ?
if (sniperTopicsVus[topicId]) return;
// Contient au moins un des mots cles ?
var mots = mot.split(',').map(function(m) { return m.trim(); }).filter(function(m) { return m.length > 0; });
var correspondance = mots.some(function(m) { return titre.indexOf(m) !== -1; });
if (!correspondance) return;
// Nouveau topic correspondant !
trouves++;
sniperTopicsVus[topicId] = true;
sauvegarderTopicsVus();
ajouterLogSniper('🎯 ' + lien.textContent.trim().substring(0, 40), 'trouve');
ajouterHistorique('trouve', {
titre: lien.textContent.trim(),
auteur: (lien.querySelector('.topic-username') || {textContent:''}).textContent.trim(),
forum: forumUrl,
topicUrl: 'https://onche.org' + href
});
// Poster dans ce topic
posterDansTopicSniper(href, message, topicId);
});
setSniperStatus('Actif — dernier scan : ' + new Date().toLocaleTimeString(), '#2ecc71');
})
.catch(function() {
setSniperStatus('Erreur de scan', '#e74c3c');
});
}
function posterDansTopicSniper(topicHref, message, topicId) {
// Construit l'URL complete
var topicUrl = 'https://onche.org' + topicHref.replace(/\/\d+$/, '/1');
if (topicHref.indexOf('https://') === 0) topicUrl = topicHref;
// Recupere un token frais depuis la page du topic
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"/);
var token = match ? match[1] : '';
if (!token) { ajouterLogSniper('❌ Token introuvable pour topic #' + topicId, 'erreur'); return; }
var actionUrl = topicUrl.split('#')[0] + '#last';
var body = new URLSearchParams();
body.append('message', message);
body.append('poll_question', '');
body.append('poll[]', '');
body.append('token', token);
return fetch(actionUrl, {
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('✅ Posté dans topic #' + topicId, 'poste');
ajouterHistorique('poste', {
topicUrl: topicUrl,
topicTitre: 'Topic #' + topicId,
message: message
});
} else if (r) {
ajouterLogSniper('❌ Erreur ' + r.status + ' pour topic #' + topicId, 'erreur');
ajouterHistorique('erreur', { topicUrl: topicUrl, topicTitre: 'Topic #' + topicId, message: message, erreur: 'HTTP ' + r.status });
}
})
.catch(function() {
ajouterLogSniper('❌ Erreur réseau pour topic #' + topicId, 'erreur');
ajouterHistorique('erreur', { topicUrl: topicUrl, topicTitre: 'Topic #' + topicId, message: message, erreur: 'Erreur reseau' });
});
}
function setSniperStatus(texte, couleur) {
var el = document.getElementById('os-sniper-status');
if (el) { el.textContent = texte; el.style.color = couleur || '#aaa'; }
}
function ajouterLogSniper(texte, type, topicUrl) {
var log = document.getElementById('os-sniper-log');
if (log) {
var ligne = document.createElement('div');
ligne.style.color = type === 'poste' ? '#2ecc71' : type === 'erreur' ? '#e74c3c' : type === 'trouve' ? '#4a90d9' : '#888';
ligne.textContent = new Date().toLocaleTimeString() + ' — ' + texte;
log.insertBefore(ligne, log.firstChild);
while (log.children.length > 20) log.removeChild(log.lastChild);
}
// Sauvegarder dans l'historique persistant
if (type) ajouterHistorique(type, texte, topicUrl);
}
// =========================================================
// Capture citation (data-message-quote)
// =========================================================
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);
// =========================================================
// Preview
// =========================================================
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 () {
var msg = document.getElementById('os-message').value;
if (!previewOuvert) {
previewBox.textContent = msg || '(message vide)';
previewBox.style.display = 'block';
this.textContent = '✕ Fermer la prévisualisation';
this.style.color = '#4a90d9';
previewOuvert = true;
document.getElementById('os-message').addEventListener('input', majPreview);
} else {
previewBox.style.display = 'none';
this.textContent = '👁 Prévisualiser';
this.style.color = '#aaa';
previewOuvert = false;
document.getElementById('os-message').removeEventListener('input', majPreview);
}
});
function majPreview() { previewBox.textContent = document.getElementById('os-message').value || '(message vide)'; }
// =========================================================
// Navigation
// =========================================================
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;
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';
var labels = { 'topic': 'Répondre 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';
});
}
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 = '';
}
});
// =========================================================
// Ajouter a la file
// =========================================================
document.getElementById('os-btn-start').addEventListener('click', function () {
var heureVal = document.getElementById('os-heure').value;
var messageVal = document.getElementById('os-message').value.trim();
var titreVal = document.getElementById('os-titre') ? document.getElementById('os-titre').value.trim() : '';
var jourVal = document.getElementById('os-jour').value;
if (!heureVal) { alert('Choisis une heure !'); return; }
if (!messageVal) { alert('Ecris un message !'); return; }
if (modeChoisi === 'nouveau' && !titreVal) { alert('Ecris un titre !'); return; }
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 ! Clique la flèche du message avant de programmer.');
return;
}
ajouterFile({
id: ++idCounter,
mode: modeChoisi,
titre: titreVal,
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 (document.getElementById('os-titre')) document.getElementById('os-titre').value = '';
document.getElementById('os-chk-jour').checked = false;
document.getElementById('os-champ-jour').style.display = 'none';
document.getElementById('os-jour').value = '';
retourListe();
});
// =========================================================
// File d'attente
// =========================================================
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 resteS = Math.floor(resteMs / 1000);
el.textContent = pad(Math.floor(resteS/3600)) + ':' + pad(Math.floor((resteS%3600)/60)) + ':' + pad(resteS%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');
var 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.length > 40 ? item.message.substring(0, 40) + '...' : item.message;
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 data-id="' + item.id + '" style="background:none;border:1px solid #c0392b;color:#c0392b;border-radius:3px;cursor:pointer;font-size:11px;padding:2px 5px;flex-shrink:0">✕</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]);
}
}
// =========================================================
// Execution
// =========================================================
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'; }
setTimeout(function () {
for (var i = 0; i < fileAttente.length; i++) {
if (fileAttente[i].id === id) { fileAttente.splice(i, 1); break; }
}
afficherFile();
sauvegarderFile();
}, 3000);
}
// =========================================================
// Poster reponse ou citation via fetch
// =========================================================
function posterAvecFetch(item, estCitation) {
// Recupere un token frais sur la page du topic
var pageUrl = item.pageUrl || window.location.href.split('#')[0];
var action = pageUrl + '#last';
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"/);
var token = match ? match[1] : '';
if (!token) { terminerItem(item.id, false, 'Token introuvable !'); return; }
var body = new URLSearchParams();
if (estCitation && item.cibleInfo && item.cibleInfo.messageId) {
body.append('answer', item.cibleInfo.messageId);
}
body.append('message', item.message);
body.append('poll_question', '');
body.append('poll[]', '');
body.append('token', token);
return fetch(action, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString(),
credentials: 'include'
});
})
.then(function(r) {
if (r && (r.ok || r.redirected)) { terminerItem(item.id, true, estCitation ? 'Citation postee !' : 'Poste !'); }
else if (r) { terminerItem(item.id, false, 'Erreur ' + r.status); }
})
.catch(function() { terminerItem(item.id, false, 'Erreur reseau !'); });
}
// =========================================================
// Creer un nouveau topic
// =========================================================
function posterNouveauTopic(item) {
var pageUrl = item.pageUrl || window.location.href.split('#')[0];
var action = pageUrl + '#post';
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"/);
var token = match ? match[1] : '';
if (!token) { 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(action, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString(),
credentials: 'include'
});
})
.then(function(r) {
if (r && (r.ok || r.redirected)) { terminerItem(item.id, true, 'Topic cree !'); }
else if (r) { terminerItem(item.id, false, 'Erreur ' + r.status); }
})
.catch(function() { terminerItem(item.id, false, 'Erreur reseau !'); });
}
// =========================================================
// Utilitaires
// =========================================================
// =========================================================
// Horloge titre onglet
// =========================================================
function mettreAJourTitre() {
var n = new Date();
document.title = pad(n.getHours()) + ':' + pad(n.getMinutes()) + ':' + pad(n.getSeconds()) + ' | Horlonche';
}
mettreAJourTitre();
setInterval(mettreAJourTitre, 1000);
})();