Migra dal codice salvato in locale al codice condiviso
This commit is contained in:
935
plugin/bot/telegram.core.js
Normal file
935
plugin/bot/telegram.core.js
Normal file
@@ -0,0 +1,935 @@
|
||||
/**
|
||||
* telegram.core.js - Bot Telegram ottimizzato per MEB SignalK
|
||||
* Gestione utenti, comandi e live updates
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const {
|
||||
encrypt,
|
||||
decrypt,
|
||||
generateToken,
|
||||
generateReadableToken,
|
||||
encryptLog,
|
||||
decryptLog,
|
||||
loadSecureFile,
|
||||
saveSecureFile
|
||||
} = require("../tools/crypt");
|
||||
|
||||
const TelegramBot = require('node-telegram-bot-api');
|
||||
|
||||
function getSK(path) {
|
||||
if (!app) return null;
|
||||
const v = app.getSelfPath(path);
|
||||
return v && v.value !== undefined && v.value !== null ? v.value : null;
|
||||
}
|
||||
|
||||
// ==================== INIZIALIZZAZIONE BOT ====================
|
||||
const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
|
||||
let bot = null;
|
||||
|
||||
function initBot() {
|
||||
if (!BOT_TOKEN) {
|
||||
console.warn("[Telegram] BOT_TOKEN non impostato: bot disabilitato.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Riusa istanza esistente se disponibile
|
||||
if (global.__meb_telegram_bot) {
|
||||
bot = global.__meb_telegram_bot;
|
||||
console.log("[Telegram] Riutilizzo istanza bot esistente");
|
||||
} else {
|
||||
bot = new TelegramBot(BOT_TOKEN, { polling: true });
|
||||
global.__meb_telegram_bot = bot;
|
||||
console.log("[Telegram] Nuova istanza bot creata");
|
||||
}
|
||||
|
||||
// Registra handlers solo una volta
|
||||
if (!global.__meb_telegram_handlers) {
|
||||
global.__meb_telegram_handlers = true;
|
||||
registerHandlers();
|
||||
console.log("[Telegram] Handlers registrati");
|
||||
}
|
||||
|
||||
return bot;
|
||||
}
|
||||
|
||||
// Inizializza all'import
|
||||
bot = initBot();
|
||||
|
||||
// ==================== CONFIGURAZIONE ====================
|
||||
const CONFIG = {
|
||||
filesPerPage: 8,
|
||||
liveUpdateInterval: 3000,
|
||||
fileExpirationTime: 10
|
||||
};
|
||||
|
||||
const telegram_users_file = path.join(__dirname, "..", "telegram_users.json");
|
||||
const logs_references_file = path.join(__dirname, "..", "datasetModels/logs_references.json");
|
||||
const authorized_admins_file = path.join(__dirname, "..", "authorized_admins.txt");
|
||||
|
||||
let app = null;
|
||||
|
||||
// Maps per gestione timer e stati
|
||||
const liveParamIntervals = new Map();
|
||||
const keyExpirationTimers = new Map();
|
||||
|
||||
// ==================== GESTIONE FILE SENSIBILI ====================
|
||||
|
||||
function loadAuthorizedAdmins() {
|
||||
try {
|
||||
if (!fs.existsSync(authorized_admins_file)) {
|
||||
return new Set();
|
||||
}
|
||||
const content = fs.readFileSync(authorized_admins_file, 'utf8');
|
||||
const admins = content
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter(line => line && !line.startsWith('#'));
|
||||
return new Set(admins);
|
||||
} catch (error) {
|
||||
console.error('[Telegram] Errore caricamento admin:', error.message);
|
||||
return new Set();
|
||||
}
|
||||
}
|
||||
|
||||
function saveAuthorizedAdmins(admins) {
|
||||
try {
|
||||
const adminArray = Array.from(admins);
|
||||
const content = '# Authorized Admin ChatIDs (one per line)\n' + adminArray.join('\n');
|
||||
fs.writeFileSync(authorized_admins_file, content, 'utf8');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[Telegram] Errore salvataggio admin:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isAdmin(chatID) {
|
||||
const admins = loadAuthorizedAdmins();
|
||||
return admins.has(String(chatID));
|
||||
}
|
||||
|
||||
function loadUsers() {
|
||||
return loadSecureFile(telegram_users_file, []);
|
||||
}
|
||||
|
||||
function saveUsers(users) {
|
||||
saveSecureFile(telegram_users_file, users);
|
||||
}
|
||||
|
||||
function loadLogsReferences() {
|
||||
return loadSecureFile(logs_references_file, { references: [] });
|
||||
}
|
||||
|
||||
function saveLogsReferences(data) {
|
||||
saveSecureFile(logs_references_file, data);
|
||||
}
|
||||
|
||||
function isAuthenticated(chatID) {
|
||||
const user = getUserByChatID(chatID);
|
||||
return user && user.hasLoggedYet;
|
||||
}
|
||||
|
||||
function createNewUser(permissions = ["basic"]) {
|
||||
const users = loadUsers();
|
||||
const newUser = {
|
||||
token: generateReadableToken(24),
|
||||
chatID: null,
|
||||
isAuthorized: permissions,
|
||||
hasLoggedYet: false
|
||||
};
|
||||
users.push(newUser);
|
||||
saveUsers(users);
|
||||
return newUser;
|
||||
}
|
||||
|
||||
function login(token, chatID) {
|
||||
const users = loadUsers();
|
||||
const userIDX = users.findIndex(u => u.token === token);
|
||||
|
||||
if (userIDX === -1) {
|
||||
throw new Error("Token non valido");
|
||||
}
|
||||
|
||||
const user = users[userIDX];
|
||||
|
||||
if (user.hasLoggedYet && user.chatID && user.chatID !== String(chatID)) {
|
||||
throw new Error("Questo token è già associato ad un altro account");
|
||||
}
|
||||
|
||||
if (!user.hasLoggedYet) {
|
||||
const newToken = generateReadableToken(32);
|
||||
user.token = newToken;
|
||||
user.hasLoggedYet = true;
|
||||
user.chatID = String(chatID);
|
||||
users[userIDX] = user;
|
||||
saveUsers(users);
|
||||
return { ...user, isFirstLogin: true, newToken };
|
||||
}
|
||||
|
||||
user.chatID = String(chatID);
|
||||
users[userIDX] = user;
|
||||
saveUsers(users);
|
||||
return { ...user, isFirstLogin: false };
|
||||
}
|
||||
|
||||
function logout(chatID) {
|
||||
const users = loadUsers();
|
||||
const userIDX = users.findIndex(u => u.chatID === String(chatID));
|
||||
|
||||
if (userIDX === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
saveUsers(users);
|
||||
return users[userIDX];
|
||||
}
|
||||
|
||||
function getUserWith(token) {
|
||||
const users = loadUsers();
|
||||
return users.find(u => u.token === token);
|
||||
}
|
||||
|
||||
function getUserByChatID(chatID) {
|
||||
const users = loadUsers();
|
||||
return users.find(u => u.chatID === String(chatID));
|
||||
}
|
||||
|
||||
async function linkBot(appInstance) {
|
||||
app = appInstance;
|
||||
if (!bot) {
|
||||
console.warn("[MEB TELEGRAM] linkBot chiamato senza TOKEN: ritorno null.");
|
||||
return null;
|
||||
}
|
||||
return bot;
|
||||
}
|
||||
|
||||
function fetchFiles(chatId, page = 0) {
|
||||
const logDirectory = path.join(__dirname, "..", "datasetModels/saved_datas");
|
||||
|
||||
try {
|
||||
const logsData = loadLogsReferences();
|
||||
const registeredFiles = new Set((logsData.references || []).map(r => r.name));
|
||||
|
||||
const items = fs.readdirSync(logDirectory);
|
||||
|
||||
const files = items.filter(item => {
|
||||
const fullPath = path.join(logDirectory, item);
|
||||
return fs.statSync(fullPath).isFile() && registeredFiles.has(item);
|
||||
});
|
||||
|
||||
if (files.length === 0) {
|
||||
bot.sendMessage(chatId, "📂 Non ci sono log salvati.");
|
||||
return;
|
||||
}
|
||||
|
||||
const sortedFiles = files
|
||||
.map(file => ({
|
||||
name: file,
|
||||
time: fs.statSync(path.join(logDirectory, file)).mtime.getTime()
|
||||
}))
|
||||
.sort((a, b) => b.time - a.time)
|
||||
.map(file => file.name);
|
||||
|
||||
const totalPages = Math.ceil(sortedFiles.length / CONFIG.filesPerPage);
|
||||
let currentPage = page;
|
||||
if (currentPage < 0) currentPage = 0;
|
||||
if (currentPage > totalPages - 1) currentPage = totalPages - 1;
|
||||
|
||||
const startIdx = currentPage * CONFIG.filesPerPage;
|
||||
const endIdx = startIdx + CONFIG.filesPerPage;
|
||||
const pageFiles = sortedFiles.slice(startIdx, endIdx);
|
||||
|
||||
const fileButtons = pageFiles.map(file => [
|
||||
{ text: `📄 ${file}`, callback_data: `request_file_${file}` }
|
||||
]);
|
||||
|
||||
const navigationButtons = [];
|
||||
|
||||
if (totalPages > 1) {
|
||||
const navRow = [];
|
||||
if (currentPage > 0) {
|
||||
navRow.push({ text: "←", callback_data: `page_${currentPage - 1}` });
|
||||
}
|
||||
navRow.push({ text: `📖 ${currentPage + 1}/${totalPages}`, callback_data: `page_info` });
|
||||
if (currentPage < totalPages - 1) {
|
||||
navRow.push({ text: "→", callback_data: `page_${currentPage + 1}` });
|
||||
}
|
||||
navigationButtons.push(navRow);
|
||||
}
|
||||
|
||||
navigationButtons.push([{ text: "Annulla", callback_data: "dismiss" }]);
|
||||
|
||||
bot.sendMessage(chatId,
|
||||
`📥 *Logs di Bordo*\n` +
|
||||
`Ogni file corrisponde ad una *sessione*. Seleziona un file per scaricarlo.\n` +
|
||||
`⚠️ Avrai solo *10 secondi* per salvare file e chiave.`,
|
||||
{
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: { inline_keyboard: [...fileButtons, ...navigationButtons] }
|
||||
}
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
bot.sendMessage(chatId, `Errore lettura directory: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentPosition() {
|
||||
if (!app) return null;
|
||||
const position = app.getSelfPath('navigation.position');
|
||||
if (!position) return null;
|
||||
return {
|
||||
latitude: position.value.latitude,
|
||||
longitude: position.value.longitude,
|
||||
};
|
||||
}
|
||||
|
||||
async function send(message) {
|
||||
if (!bot) return;
|
||||
const users = loadUsers();
|
||||
const loggedUsers = users.filter(u => u.hasLoggedYet && u.chatID);
|
||||
|
||||
for (const user of loggedUsers) {
|
||||
try {
|
||||
await bot.sendMessage(user.chatID, message);
|
||||
} catch (error) {
|
||||
console.error(`[Telegram] Send error to ${user.chatID}:`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== RENDER FUNCTIONS ====================
|
||||
|
||||
function renderPositionText() {
|
||||
if (!app) return "❌ App non disponibile";
|
||||
|
||||
const pos = app.getSelfPath('navigation.position')?.value;
|
||||
const sog = getSK('navigation.speedOverGround');
|
||||
const cog = getSK('navigation.courseOverGroundTrue');
|
||||
const heading = getSK('navigation.headingTrue');
|
||||
|
||||
const lat = pos?.latitude?.toFixed(5) ?? "N/A";
|
||||
const lon = pos?.longitude?.toFixed(5) ?? "N/A";
|
||||
const speed = sog != null ? (sog * 1.94384).toFixed(1) : "N/A"; // m/s to knots
|
||||
const course = cog != null ? (cog * 180 / Math.PI).toFixed(0) : "N/A"; // rad to deg
|
||||
const headingDeg = heading != null ? (heading * 180 / Math.PI).toFixed(0) : "N/A";
|
||||
|
||||
return `📍 *Posizione & Velocità*\n\n` +
|
||||
`Latitudine: \`${lat}\`\n` +
|
||||
`Longitudine: \`${lon}\`\n` +
|
||||
`SOG: ${speed} kn\n` +
|
||||
`COG: ${course}°\n` +
|
||||
`Heading: ${headingDeg}°`;
|
||||
}
|
||||
|
||||
function renderWindText() {
|
||||
const speed = getSK('meb.wind.speed');
|
||||
const direction = getSK('meb.wind.direction');
|
||||
|
||||
return `🌬️ *Vento*\n\n` +
|
||||
`Velocità: ${speed} km/h\n` +
|
||||
`Direzione: ${direction}°\n`;
|
||||
}
|
||||
|
||||
function renderWavesText() {
|
||||
|
||||
const height = getSK('meb.waves.height');
|
||||
const period = getSK('meb.waves.period');
|
||||
const dir = getSK('meb.waves.direction');
|
||||
|
||||
return `🌊 *Onde*\n\n` +
|
||||
`Altezza: ${height} m\n` +
|
||||
`Periodo: ${period} s\n` +
|
||||
`Direzione: ${dir}°`;
|
||||
}
|
||||
|
||||
function renderForecastsText() {
|
||||
const temp = getSK('meb.temperature');
|
||||
const humidity = getSK('meb.humidity');
|
||||
const pressure = getSK('meb.pressure');
|
||||
const rain = getSK('meb.precipitation');
|
||||
return `⛅️ *Previsioni Meteo*\n\n` +
|
||||
`Temperatura: ${temp} °C\n` +
|
||||
`Umidità: ${humidity} %\n` +
|
||||
`Pressione: ${pressure} hPa\n`;
|
||||
}
|
||||
|
||||
function renderBatteriesText() {
|
||||
|
||||
const voltage = getSK('electrical.batteries.traction.voltage');
|
||||
const current = getSK('electrical.batteries.traction.current');
|
||||
const soc = getSK('electrical.batteries.traction.stateOfCharge');
|
||||
const power = getSK('electrical.batteries.traction.power');
|
||||
|
||||
return `🔋 *Batterie*\n\n` +
|
||||
`Tensione: ${voltage?.toFixed(1) ?? "N/A"} V\n` +
|
||||
`Corrente: ${current?.toFixed(1) ?? "N/A"} A\n` +
|
||||
`SOC: ${soc != null ? (soc * 100).toFixed(0) : "N/A"} %\n` +
|
||||
`Potenza: ${power?.toFixed(0) ?? "N/A"} W`;
|
||||
}
|
||||
|
||||
function renderDashboardText() {
|
||||
const posText = renderPositionText()
|
||||
const windText = renderWindText()
|
||||
const wavesText = renderWavesText()
|
||||
const forecastText = renderForecastsText()
|
||||
const battText = renderBatteriesText()
|
||||
|
||||
return `📊 *Dashboard Completa*\n` +
|
||||
`\n${posText}\n\n` +
|
||||
`\n${forecastText}\n\n` +
|
||||
`\n${windText}\n\n` +
|
||||
`\n${wavesText}\n\n` +
|
||||
`\n${battText}`;
|
||||
}
|
||||
|
||||
// ==================== REGISTRAZIONE HANDLERS ====================
|
||||
|
||||
function registerHandlers() {
|
||||
if (!bot) return;
|
||||
|
||||
// Handler: /start
|
||||
bot.onText(/\/start/, (msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
|
||||
if (isAuthenticated(chatId)) {
|
||||
const menu = {
|
||||
keyboard: [
|
||||
[{ text: "📊 Dashboard" }],
|
||||
[{ text: "Parametri di Bordo" }],
|
||||
[{ text: "File di Logs" }],
|
||||
[{ text: "Genera un nuovo log" }],
|
||||
[{ text: "Stato dei Log" }]
|
||||
],
|
||||
resize_keyboard: true,
|
||||
one_time_keyboard: false
|
||||
};
|
||||
|
||||
bot.sendMessage(chatId,
|
||||
"Benvenuto nel Data Console.\n" +
|
||||
"• Visualizza i dati del computer di bordo\n" +
|
||||
"• Ricevi aggiornamenti su parametri a scelta\n" +
|
||||
"• Scarica i file di log della barca",
|
||||
{ parse_mode: 'Markdown', reply_markup: menu }
|
||||
);
|
||||
} else {
|
||||
bot.sendMessage(chatId,
|
||||
"Benvenuto nel MEB Data Console!\n" +
|
||||
"Per accedere ai dati è necessario un token di accesso.",
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
|
||||
bot.sendMessage(chatId, "👤 Login", {
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: "❓ Come ottengo un token", callback_data: "token_login_question" }],
|
||||
[{ text: "🔑 Ho un token", callback_data: "token_ready" }]
|
||||
]
|
||||
},
|
||||
parse_mode: 'Markdown'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Menu testuale
|
||||
bot.onText(/📊 Dashboard/, (msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
if (!isAuthenticated(chatId)) {
|
||||
bot.sendMessage(chatId, "Effettua prima il login con /login <token>");
|
||||
return;
|
||||
}
|
||||
|
||||
const dashboardMsg = renderDashboardText();
|
||||
bot.sendMessage(chatId, dashboardMsg, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: "🔄 Aggiorna", callback_data: "refresh_dashboard" }],
|
||||
[{ text: "📡 Live (3s)", callback_data: "live_dashboard" }]
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
bot.onText(/File di Logs/, (msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
if (!isAuthenticated(chatId)) {
|
||||
bot.sendMessage(chatId, "Effettua prima il login con /login <token>");
|
||||
return;
|
||||
}
|
||||
fetchFiles(chatId, 0);
|
||||
});
|
||||
|
||||
bot.onText(/Parametri di Bordo/, (msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
|
||||
if (!isAuthenticated(chatId)) {
|
||||
bot.sendMessage(chatId, "Effettua il login con /login <token>");
|
||||
return;
|
||||
}
|
||||
|
||||
bot.sendMessage(chatId, "*Parametri di Bordo*\nQui potrai visualizzare i parametri attuali del computer di bordo. Scegli il parametro che vuoi visualizzare dal menu qui sotto.", {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: "📊 Dashboard", callback_data: "get_dashboard" }],
|
||||
[{ text: "⛅️ Meteo", callback_data: "get_forecasts" }],
|
||||
[{ text: "📍 Posizione", callback_data: "get_position" }],
|
||||
[{ text: "🌬️ Vento", callback_data: "get_wind" }],
|
||||
[{ text: "🌊 Onde", callback_data: "get_waves" }],
|
||||
[{ text: "🔋 Batterie", callback_data: "get_batteries" }],
|
||||
[{ text: "Annulla", callback_data: "dismiss" }]
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Login
|
||||
bot.onText(/\/login\s+(.+)/, (msg, match) => {
|
||||
const chatID = msg.chat.id;
|
||||
const token = (match && match[1] || "").trim();
|
||||
|
||||
if (!token) {
|
||||
bot.sendMessage(chatID, "Inserisci il token: /login <token>");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = login(token, chatID);
|
||||
if (!result) {
|
||||
bot.sendMessage(chatID, "Token non valido.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.isFirstLogin) {
|
||||
bot.sendMessage(chatID,
|
||||
`*Primo accesso completato!*\n\n` +
|
||||
`Il tuo nuovo token permanente:\n\`${result.newToken}\`\n\n` +
|
||||
`Salvalo! Non potrà essere usato da altri account.`,
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
} else {
|
||||
bot.sendMessage(chatID, "✅ Login effettuato!");
|
||||
}
|
||||
|
||||
const menu = {
|
||||
keyboard: [
|
||||
[{ text: "📊 Dashboard" }],
|
||||
[{ text: "Parametri di Bordo" }],
|
||||
[{ text: "File di Logs" }],
|
||||
[{ text: "Genera un nuovo log" }],
|
||||
[{ text: "Stato dei Log" }]
|
||||
],
|
||||
resize_keyboard: true
|
||||
};
|
||||
|
||||
bot.sendMessage(chatID, "Menu principale:", { reply_markup: menu });
|
||||
} catch (error) {
|
||||
bot.sendMessage(chatID, `❌ ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
bot.onText(/\/logout/, (msg) => {
|
||||
const chatID = msg.chat.id;
|
||||
const user = logout(chatID);
|
||||
if (!user) {
|
||||
bot.sendMessage(chatID, "Non sei loggato.");
|
||||
return;
|
||||
}
|
||||
bot.sendMessage(chatID, "Logout effettuato. Usa /login <token> per rientrare.");
|
||||
});
|
||||
|
||||
// Admin commands
|
||||
bot.onText(/\/newuser(?:\s+(.*))?/, (msg, match) => {
|
||||
const chatID = msg.chat.id;
|
||||
|
||||
if (!isAdmin(chatID)) {
|
||||
bot.sendMessage(chatID, "⛔ Non autorizzato.");
|
||||
return;
|
||||
}
|
||||
|
||||
const permissionsArg = (match && match[1] || "").trim();
|
||||
const permissions = permissionsArg
|
||||
? permissionsArg.split(',').map(p => p.trim()).filter(p => p)
|
||||
: ["basic"];
|
||||
|
||||
try {
|
||||
const newUser = createNewUser(permissions);
|
||||
bot.sendMessage(chatID,
|
||||
`✅ *Nuovo utente creato*\n\nToken: \`${newUser.token}\``,
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
} catch (error) {
|
||||
bot.sendMessage(chatID, `❌ ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
bot.onText(/\/addadmin\s+(\d+)/, (msg, match) => {
|
||||
const chatID = msg.chat.id;
|
||||
const newAdminID = match && match[1];
|
||||
|
||||
if (!isAdmin(chatID)) {
|
||||
bot.sendMessage(chatID, "⛔ Non autorizzato.");
|
||||
return;
|
||||
}
|
||||
|
||||
const admins = loadAuthorizedAdmins();
|
||||
if (admins.has(newAdminID)) {
|
||||
bot.sendMessage(chatID, "Già admin.");
|
||||
return;
|
||||
}
|
||||
|
||||
admins.add(newAdminID);
|
||||
saveAuthorizedAdmins(admins);
|
||||
bot.sendMessage(chatID, `✅ Admin \`${newAdminID}\` aggiunto.`, { parse_mode: 'Markdown' });
|
||||
});
|
||||
|
||||
bot.onText(/\/removeadmin\s+(\d+)/, (msg, match) => {
|
||||
const chatID = msg.chat.id;
|
||||
const adminToRemove = match && match[1];
|
||||
|
||||
if (!isAdmin(chatID)) {
|
||||
bot.sendMessage(chatID, "⛔ Non autorizzato.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (adminToRemove === String(chatID)) {
|
||||
bot.sendMessage(chatID, "Non puoi rimuovere te stesso.");
|
||||
return;
|
||||
}
|
||||
|
||||
const admins = loadAuthorizedAdmins();
|
||||
if (!admins.has(adminToRemove)) {
|
||||
bot.sendMessage(chatID, "Non è admin.");
|
||||
return;
|
||||
}
|
||||
|
||||
admins.delete(adminToRemove);
|
||||
saveAuthorizedAdmins(admins);
|
||||
bot.sendMessage(chatID, `✅ Admin \`${adminToRemove}\` rimosso.`, { parse_mode: 'Markdown' });
|
||||
});
|
||||
|
||||
bot.onText(/\/listusers/, (msg) => {
|
||||
const chatID = msg.chat.id;
|
||||
|
||||
if (!isAdmin(chatID)) {
|
||||
bot.sendMessage(chatID, "⛔ Non autorizzato.");
|
||||
return;
|
||||
}
|
||||
|
||||
const users = loadUsers();
|
||||
if (users.length === 0) {
|
||||
bot.sendMessage(chatID, "Nessun utente.");
|
||||
return;
|
||||
}
|
||||
|
||||
let message = `👥 *Utenti:* ${users.length}\n\n`;
|
||||
users.forEach((user, idx) => {
|
||||
const status = user.hasLoggedYet ? '✅' : '⏳';
|
||||
message += `${idx + 1}. ${status} \`${user.chatID || 'N/A'}\`\n`;
|
||||
});
|
||||
|
||||
bot.sendMessage(chatID, message, { parse_mode: 'Markdown' });
|
||||
});
|
||||
|
||||
bot.onText(/\/mychatid/, (msg) => {
|
||||
bot.sendMessage(msg.chat.id, `ChatID: \`${msg.chat.id}\``, { parse_mode: 'Markdown' });
|
||||
});
|
||||
|
||||
// Interval control
|
||||
bot.onText(/\/changei\s+(log|api)\s+(\d+)/, (msg, match) => {
|
||||
const chatID = msg.chat.id;
|
||||
|
||||
if (!isAdmin(chatID)) {
|
||||
bot.sendMessage(chatID, "⛔ Non autorizzato.");
|
||||
return;
|
||||
}
|
||||
|
||||
const type = match[1];
|
||||
const seconds = parseInt(match[2], 10);
|
||||
|
||||
if (isNaN(seconds) || seconds < 1) {
|
||||
bot.sendMessage(chatID, "❌ Secondi non validi (min 1).");
|
||||
return;
|
||||
}
|
||||
|
||||
const newIntervalMs = seconds * 1000;
|
||||
|
||||
// Debug: verifica stato app
|
||||
if (!app) {
|
||||
bot.sendMessage(chatID, "❌ App non inizializzata. Riprova tra qualche secondo.");
|
||||
console.error('[Telegram] app è null in change_interval');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!app.intervalControl) {
|
||||
bot.sendMessage(chatID, "❌ Sistema intervalControl non disponibile. Il plugin potrebbe non essere ancora avviato.");
|
||||
console.error('[Telegram] app.intervalControl non esiste');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = app.intervalControl.updateInterval(type, newIntervalMs);
|
||||
|
||||
if (result) {
|
||||
const typeLabel = type === 'log' ? 'Log recording' : 'OpenMeteo API';
|
||||
bot.sendMessage(chatID,
|
||||
`✅ *${typeLabel}* aggiornato a *${seconds}s*`,
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
} else {
|
||||
bot.sendMessage(chatID, "❌ Tipo non valido. Usa: `log` o `api`", { parse_mode: 'Markdown' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Telegram] Errore change_interval:', error);
|
||||
bot.sendMessage(chatID, `❌ Errore: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
bot.onText(/\/intervals/, (msg) => {
|
||||
const chatID = msg.chat.id;
|
||||
|
||||
if (!isAdmin(chatID)) {
|
||||
bot.sendMessage(chatID, "⛔ Non autorizzato.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!app) {
|
||||
bot.sendMessage(chatID, "❌ App non inizializzata.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!app.intervalControl) {
|
||||
bot.sendMessage(chatID, "❌ Sistema intervalControl non disponibile.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const intervals = app.intervalControl.getIntervals();
|
||||
|
||||
bot.sendMessage(chatID,
|
||||
`⏱️ *Intervalli Attuali*\n\n` +
|
||||
`📝 Log: *${intervals.log_interval / 1000}s*\n` +
|
||||
`🌤️ API: *${intervals.openmeteo_interval / 1000}s*\n\n` +
|
||||
`Per modificare:\n` +
|
||||
`\`/changei log <sec>\`\n` +
|
||||
`\`/changei api <sec>\``,
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('[Telegram] Errore intervals:', error);
|
||||
bot.sendMessage(chatID, `❌ Errore: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Callback query handler
|
||||
bot.on('callback_query', async (query) => {
|
||||
const chatId = query.message.chat.id;
|
||||
const messageId = query.message.message_id;
|
||||
const data = query.data;
|
||||
|
||||
await bot.answerCallbackQuery(query.id);
|
||||
|
||||
if (!isAuthenticated(chatId) && !['token_login_question', 'token_ready'].includes(data)) {
|
||||
bot.sendMessage(chatId, "Effettua prima il login.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case 'dismiss':
|
||||
bot.deleteMessage(chatId, messageId).catch(() => {});
|
||||
break;
|
||||
|
||||
case 'token_login_question':
|
||||
bot.sendMessage(chatId,
|
||||
"Per ottenere un token, contatta un amministratore del sistema."
|
||||
);
|
||||
break;
|
||||
|
||||
case 'token_ready':
|
||||
bot.sendMessage(chatId, "Usa: /login <token>");
|
||||
break;
|
||||
|
||||
case 'get_dashboard':
|
||||
case 'refresh_dashboard':
|
||||
bot.editMessageText(renderDashboardText(), {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: "🔄 Aggiorna", callback_data: "refresh_dashboard" }],
|
||||
[{ text: "📡 Live (3s)", callback_data: "live_dashboard" }],
|
||||
[{ text: "⏹️ Chiudi", callback_data: "dismiss" }]
|
||||
]
|
||||
}
|
||||
}).catch(() => {});
|
||||
break;
|
||||
|
||||
case 'live_dashboard':
|
||||
// Ferma eventuali live precedenti
|
||||
if (liveParamIntervals.has(chatId)) {
|
||||
clearInterval(liveParamIntervals.get(chatId));
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
bot.editMessageText(renderDashboardText(), {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: "⏹️ Stop Live", callback_data: "stop_live" }]
|
||||
]
|
||||
}
|
||||
}).catch(() => {
|
||||
clearInterval(interval);
|
||||
liveParamIntervals.delete(chatId);
|
||||
});
|
||||
}, CONFIG.liveUpdateInterval);
|
||||
|
||||
liveParamIntervals.set(chatId, interval);
|
||||
break;
|
||||
|
||||
case 'stop_live':
|
||||
if (liveParamIntervals.has(chatId)) {
|
||||
clearInterval(liveParamIntervals.get(chatId));
|
||||
liveParamIntervals.delete(chatId);
|
||||
}
|
||||
bot.editMessageText(renderDashboardText(), {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: "🔄 Aggiorna", callback_data: "refresh_dashboard" }],
|
||||
[{ text: "📡 Live (3s)", callback_data: "live_dashboard" }],
|
||||
[{ text: "⏹️ Chiudi", callback_data: "dismiss" }]
|
||||
]
|
||||
}
|
||||
}).catch(() => {});
|
||||
break;
|
||||
|
||||
case 'get_position':
|
||||
bot.editMessageText(renderPositionText(), {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: { inline_keyboard: [[{ text: "← Indietro", callback_data: "back_to_params" }]] }
|
||||
}).catch(() => {});
|
||||
break;
|
||||
|
||||
case 'get_wind':
|
||||
bot.editMessageText(renderWindText(), {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: { inline_keyboard: [[{ text: "← Indietro", callback_data: "back_to_params" }]] }
|
||||
}).catch(() => {});
|
||||
break;
|
||||
|
||||
case 'get_waves':
|
||||
bot.editMessageText(renderWavesText(), {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: { inline_keyboard: [[{ text: "← Indietro", callback_data: "back_to_params" }]] }
|
||||
}).catch(() => {});
|
||||
break;
|
||||
|
||||
case 'get_forecasts':
|
||||
bot.editMessageText(renderForecastsText(), {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: { inline_keyboard: [[{ text: "← Indietro", callback_data: "back_to_params" }]] }
|
||||
}).catch(() => {});
|
||||
break;
|
||||
|
||||
case 'get_batteries':
|
||||
bot.editMessageText(renderBatteriesText(), {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: { inline_keyboard: [[{ text: "← Indietro", callback_data: "back_to_params" }]] }
|
||||
}).catch(() => {});
|
||||
break;
|
||||
|
||||
case 'back_to_params':
|
||||
bot.editMessageText("*Parametri di Bordo*\nQui potrai visualizzare i parametri attuali del computer di bordo. Scegli il parametro che vuoi visualizzare dal menu qui sotto.", {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: "📊 Dashboard Completa", callback_data: "get_dashboard" }],
|
||||
[{ text: "⛅️ Meteo", callback_data: "get_forecasts" }],
|
||||
[{ text: "📍 Posizione", callback_data: "get_position" }],
|
||||
[{ text: "🌬️ Vento", callback_data: "get_wind" }],
|
||||
[{ text: "🌊 Onde", callback_data: "get_waves" }],
|
||||
[{ text: "🔋 Batterie", callback_data: "get_batteries" }],
|
||||
[{ text: "Annulla", callback_data: "dismiss" }]
|
||||
]
|
||||
}
|
||||
}).catch(() => {});
|
||||
break;
|
||||
|
||||
default:
|
||||
// Gestione paginazione file
|
||||
if (data.startsWith('page_')) {
|
||||
const page = parseInt(data.replace('page_', ''), 10);
|
||||
if (!isNaN(page)) {
|
||||
bot.deleteMessage(chatId, messageId).catch(() => {});
|
||||
fetchFiles(chatId, page);
|
||||
}
|
||||
}
|
||||
// Gestione richiesta file
|
||||
else if (data.startsWith('request_file_')) {
|
||||
const fileName = data.replace('request_file_', '');
|
||||
const filePath = path.join(__dirname, "..", "datasetModels/saved_datas", fileName);
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
const logsData = loadLogsReferences();
|
||||
const fileRef = (logsData.references || []).find(r => r.name === fileName);
|
||||
const key = fileRef?.key || "Chiave non trovata";
|
||||
|
||||
try {
|
||||
const fileMsg = await bot.sendDocument(chatId, filePath, {
|
||||
caption: `🔑 Chiave: \`${key}\`\n⚠️ Questo messaggio verrà eliminato tra 10 secondi.`,
|
||||
parse_mode: 'Markdown'
|
||||
});
|
||||
|
||||
// Elimina dopo 10 secondi
|
||||
setTimeout(() => {
|
||||
bot.deleteMessage(chatId, fileMsg.message_id).catch(() => {});
|
||||
}, CONFIG.fileExpirationTime * 1000);
|
||||
|
||||
} catch (error) {
|
||||
bot.sendMessage(chatId, `❌ Errore invio file: ${error.message}`);
|
||||
}
|
||||
} else {
|
||||
bot.sendMessage(chatId, "❌ File non trovato.");
|
||||
}
|
||||
|
||||
bot.deleteMessage(chatId, messageId).catch(() => {});
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
} // Fine registerHandlers
|
||||
|
||||
module.exports = {
|
||||
linkBot,
|
||||
send,
|
||||
loadUsers,
|
||||
saveUsers,
|
||||
getUserByChatID,
|
||||
isAuthenticated,
|
||||
isAdmin
|
||||
};
|
||||
|
||||
|
||||
|
||||
24
plugin/bot/telegram_users.json
Normal file
24
plugin/bot/telegram_users.json
Normal file
@@ -0,0 +1,24 @@
|
||||
[
|
||||
{
|
||||
"token": "eccef678c73b825fd2af7a3ce76603aeef68c6280862f1c2",
|
||||
"hasLogged": true,
|
||||
"chatId": 5868470977,
|
||||
"chatID": 5868470977
|
||||
},
|
||||
{
|
||||
"token": "5A6MjMd6amSGgZbk6PZ9T9sdJKjWwbHM",
|
||||
"chatID": "5868470977",
|
||||
"isAuthorized": [
|
||||
"basic"
|
||||
],
|
||||
"hasLoggedYet": true
|
||||
},
|
||||
{
|
||||
"token": "af9aBSY9taEedmZXFhy3Fhns3VHtXSxT",
|
||||
"chatID": "838642766",
|
||||
"isAuthorized": [
|
||||
"basic"
|
||||
],
|
||||
"hasLoggedYet": true
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user