183 lines
7.0 KiB
JavaScript
183 lines
7.0 KiB
JavaScript
// ==UserScript==
|
|
// @name OTP NTFY Console (Token)
|
|
// @namespace http://tampermonkey.net/
|
|
// @version 2.2
|
|
// @description Pannello OTP sicuro con Bearer Token e monitoraggio live
|
|
// @author Gemini + Gatto
|
|
// @match *://www.ilportaledellautomobilista.it/*
|
|
// @grant GM_notification
|
|
// @grant GM_setClipboard
|
|
// ==/UserScript==
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
// --- CONFIGURAZIONE SICURA ---
|
|
const BASE_URL = "ntfy.dati-web.it";
|
|
// <--- INCOLLA QUI IL TUO TOKEN tk_...
|
|
const NTFY_TOKEN = "tk_mh1ox5scgkorten10a7baelpehh6p";
|
|
|
|
let currentTopic = localStorage.getItem('otp_topic');
|
|
let mode = localStorage.getItem('otp_mode');
|
|
const OTP_REGEX = /(?:^|\s)(\S{8})(?=\s|$)/;
|
|
|
|
function injectInterface() {
|
|
const logo = document.getElementById('logo-portale');
|
|
if (!logo) {
|
|
setTimeout(injectInterface, 500);
|
|
return;
|
|
}
|
|
|
|
const parentLink = logo.closest('a');
|
|
const anchorPoint = parentLink ? parentLink : logo;
|
|
|
|
if (document.getElementById('otp-embedded-panel')) return;
|
|
|
|
const navPanel = document.createElement('div');
|
|
navPanel.id = "otp-embedded-panel";
|
|
|
|
// Stile del pannello
|
|
navPanel.style = `
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
margin-left: 330px;
|
|
margin-top: 16px;
|
|
padding: 0 10px;
|
|
background: #ffffff;
|
|
border: 1px dashed #0056b3;
|
|
border-radius: 6px;
|
|
vertical-align: middle;
|
|
height: 60px;
|
|
font-family: Arial, sans-serif;
|
|
box-sizing: border-box;
|
|
cursor: default;
|
|
pointer-events:auto;
|
|
position: relative;
|
|
z-index: 10;
|
|
min-width: 340px !important;
|
|
max-width: 420px !important;
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1) !important;
|
|
line-height: normal;
|
|
color: #333 !important;
|
|
` ;
|
|
navPanel.innerHTML = `
|
|
<div style="display: flex; flex-direction: column; gap: 4px;">
|
|
<input type="text" id="topicInput" value="${currentTopic}" placeholder="Topic" style="margin-left: 2px; width: 100px; height: 20px; font-size: 10px; border: 1px solid #ccc; border-radius: 3px; padding: 1px 1px;">
|
|
<button id="saveConfig" style="margin-left: 2px; width: 102px; height: 22px; font-size: 10px; background: #0056b3; color: white; border: 1px solid; border-radius: 3px; cursor: pointer; font-weight: bold;">AGG. TOPIC</button>
|
|
</div>
|
|
|
|
<div style="margin-left: 15px; border-left: 1px solid #ccc; height: 40px;"></div>
|
|
|
|
<div style="display: flex; flex-direction: column; gap: 2px; font-size: 10px; color: #333;">
|
|
<label style="width: 70px; cursor:pointer; display: flex; align-items: left; gap: 2px;">
|
|
<input type="radio" name="otpMode" value="notify" ${mode==='notify'?'checked':''}> Notifica
|
|
</label>
|
|
<label style="width: 70px; cursor:pointer; display: flex; align-items: left; gap: 2px;">
|
|
<input type="radio" name="otpMode" value="type" ${mode==='type'?'checked':''}> Digitaz.
|
|
</label>
|
|
</div>
|
|
|
|
<div style="margin-left: 15px; margin-right: 8px; border-left: 1px solid #ccc; height: 40px;"></div>
|
|
|
|
<div style="text-align: center; flex-grow: 1;">
|
|
<div id="lastCodeLabel" style="font-size: 18px; font-weight: bold; color: #d9534f; letter-spacing: 1px;">---</div>
|
|
<div id="statusLabel" style="font-size: 9px; color: #28a745; font-weight: bold;">STABILE</div>
|
|
</div>
|
|
`;
|
|
|
|
anchorPoint.parentNode.insertBefore(navPanel, anchorPoint.nextSibling);
|
|
|
|
// Salvataggio impostazioni
|
|
document.getElementById('saveConfig').addEventListener('click', () => {
|
|
localStorage.setItem('otp_topic', document.getElementById('topicInput').value.trim());
|
|
localStorage.setItem('otp_mode', document.querySelector('input[name="otpMode"]:checked').value);
|
|
location.reload();
|
|
});
|
|
|
|
startListening();
|
|
}
|
|
|
|
async function startListening() {
|
|
const statusLabel = document.getElementById('statusLabel');
|
|
|
|
try {
|
|
const response = await fetch(`https://${BASE_URL}/${currentTopic}/json`, {
|
|
headers: {
|
|
"Authorization": `Bearer ${NTFY_TOKEN}`
|
|
}
|
|
});
|
|
|
|
if (response.status === 401 || response.status === 403) {
|
|
statusLabel.innerText = "ERRORE TOKEN";
|
|
statusLabel.style.color = "red";
|
|
return;
|
|
}
|
|
|
|
if (!response.ok) throw new Error("Errore connessione");
|
|
|
|
statusLabel.innerText = "ONLINE";
|
|
statusLabel.style.color = "#28a745";
|
|
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
|
|
while (true) {
|
|
const { value, done } = await reader.read();
|
|
if (done) break;
|
|
|
|
const chunk = decoder.decode(value);
|
|
const lines = chunk.split('\n');
|
|
|
|
for (const line of lines) {
|
|
if (!line.trim()) continue;
|
|
try {
|
|
const data = JSON.parse(line);
|
|
if (data.event === 'message' && data.message) {
|
|
const match = data.message.match(OTP_REGEX);
|
|
if (match) handleOTP(match[1]);
|
|
}
|
|
} catch (e) { console.error("Errore parsing JSON", e); }
|
|
}
|
|
}
|
|
} catch (err) {
|
|
statusLabel.innerText = "RE-CONNECTING...";
|
|
statusLabel.style.color = "orange";
|
|
setTimeout(startListening, 5000);
|
|
}
|
|
}
|
|
|
|
function handleOTP(newOtp) {
|
|
document.getElementById('lastCodeLabel').innerText = newOtp;
|
|
GM_setClipboard(newOtp);
|
|
|
|
// Notifica di sistema
|
|
GM_notification({
|
|
title: "OTP Infocert",
|
|
text: `Codice: ${newOtp} (Copiato)`,
|
|
timeout: 5000
|
|
});
|
|
|
|
// Se la modalità è "Digita", inserisce l'OTP nel campo attualmente selezionato (focus)
|
|
if (document.querySelector('input[name="otpMode"]:checked').value === 'type') {
|
|
const activeInput = document.activeElement;
|
|
|
|
// Verifichiamo che l'elemento attivo sia effettivamente un campo di testo
|
|
if (activeInput && (activeInput.tagName === 'INPUT' || activeInput.tagName === 'TEXTAREA')) {
|
|
activeInput.value = newOtp;
|
|
|
|
// Simula l'evento di input per attivare eventuali validazioni/script della pagina
|
|
activeInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
activeInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
} else {
|
|
console.warn("Nessun campo di testo selezionato al momento dell'inserimento.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (document.readyState === "complete" || document.readyState === "interactive") {
|
|
injectInterface();
|
|
} else {
|
|
document.addEventListener("DOMContentLoaded", injectInterface);
|
|
}
|
|
})(); |