Aggiunto collegamento al server
This commit is contained in:
152
plugin/telegram/callbacks/dashboard.js
Normal file
152
plugin/telegram/callbacks/dashboard.js
Normal file
@@ -0,0 +1,152 @@
|
||||
// Mappa globale per salvare gli interval id anche dopo un "hot-reload"
|
||||
if (!global.__meb_live_dashboards) {
|
||||
global.__meb_live_dashboards = new Map();
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
id: 'dashboard-refresh',
|
||||
execute: async ({ bot, chatId, msg }) => {
|
||||
const dash = require('../commands/dashboard.js');
|
||||
const newText = dash.formatSensorData();
|
||||
|
||||
try {
|
||||
await bot.editMessageText(newText, {
|
||||
chat_id: chatId,
|
||||
message_id: msg.message_id,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[
|
||||
{ text: "⚡️ Avvia Live Tracker (2s)", callback_data: 'dashboard-live-start' }
|
||||
],
|
||||
[
|
||||
{ text: "🔄 Ricarica Dati", callback_data: 'dashboard-refresh' }
|
||||
]
|
||||
]
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (!e.message.includes('message is not modified')) {
|
||||
console.error("Errore nel refresh dashboard:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'dashboard-live-start',
|
||||
execute: async ({ bot, chatId, msg }) => {
|
||||
const dash = require('../commands/dashboard.js');
|
||||
|
||||
const messageId = msg.message_id;
|
||||
const liveKey = `${chatId}_${messageId}`;
|
||||
|
||||
// Se è già attivo un live per questo messaggio, non fare nulla
|
||||
if (global.__meb_live_dashboards.has(liveKey)) return;
|
||||
|
||||
// Avvisa che sta partendo
|
||||
const startMarkup = {
|
||||
inline_keyboard: [
|
||||
[
|
||||
{ text: "🛑 Ferma Live Tracker", callback_data: 'dashboard-live-stop' }
|
||||
]
|
||||
]
|
||||
};
|
||||
|
||||
await bot.editMessageReplyMarkup(startMarkup, { chat_id: chatId, message_id: messageId });
|
||||
|
||||
// Inizializza l'interval a 2 secondi. Autodistruzione dopo 30s
|
||||
let count = 15; // 15 tick da 2 secondi = 30 secondi
|
||||
const intervalTimer = setInterval(async () => {
|
||||
count--;
|
||||
const baseText = dash.formatSensorData();
|
||||
|
||||
// Se il tempo scade, disattiva il live e ripristina i tasti normali
|
||||
if (count <= 0) {
|
||||
if (global.__meb_live_dashboards.has(liveKey)) {
|
||||
clearInterval(global.__meb_live_dashboards.get(liveKey));
|
||||
global.__meb_live_dashboards.delete(liveKey);
|
||||
}
|
||||
try {
|
||||
await bot.editMessageText(baseText + `\n🛑 _Live tracker terminato automaticamente (30s) per risparmiare risorse._`, {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: "⚡️ Avvia Live Tracker (2s)", callback_data: 'dashboard-live-start' }],
|
||||
[{ text: "🔄 Ricarica Dati", callback_data: 'dashboard-refresh' }]
|
||||
]
|
||||
}
|
||||
});
|
||||
} catch (e) { }
|
||||
return;
|
||||
}
|
||||
|
||||
// Altrimenti prosegui con l'aggiornamento e la stringa del countdown
|
||||
const newText = baseText + `\n⏳ _Live attivo: arresto automatico tra *${count * 2}s*_`;
|
||||
|
||||
try {
|
||||
await bot.editMessageText(newText, {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: startMarkup
|
||||
});
|
||||
} catch (e) {
|
||||
// API limits o the message was not modified
|
||||
if (e.response && e.response.statusCode === 400 && e.message.includes("message is not modified")) {
|
||||
// ignore
|
||||
} else if (e.response && e.response.statusCode === 429) {
|
||||
// Troppe richieste Telegram
|
||||
console.warn("[Telegram Dashboard] Rate Limit raggionto. Riprovo più tardi...");
|
||||
} else if (e.response && e.response.statusCode === 400 && e.message.includes("message to edit not found")) {
|
||||
// Il messaggio è stato cancellato dall'utente
|
||||
clearInterval(intervalTimer);
|
||||
global.__meb_live_dashboards.delete(liveKey);
|
||||
} else {
|
||||
console.error("[Telegram Dashboard] Errore update live:", e);
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
global.__meb_live_dashboards.set(liveKey, intervalTimer);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'dashboard-live-stop',
|
||||
execute: async ({ bot, chatId, msg }) => {
|
||||
const dash = require('../commands/dashboard.js');
|
||||
|
||||
const messageId = msg.message_id;
|
||||
const liveKey = `${chatId}_${messageId}`;
|
||||
|
||||
// Pulisci l'interval se esiste
|
||||
if (global.__meb_live_dashboards.has(liveKey)) {
|
||||
clearInterval(global.__meb_live_dashboards.get(liveKey));
|
||||
global.__meb_live_dashboards.delete(liveKey);
|
||||
}
|
||||
|
||||
// Ripristina la formattazione iniziale
|
||||
const newText = dash.formatSensorData();
|
||||
|
||||
try {
|
||||
await bot.editMessageText(newText, {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[
|
||||
{ text: "⚡️ Avvia Live Tracker (2s)", callback_data: 'dashboard-live-start' }
|
||||
],
|
||||
[
|
||||
{ text: "🔄 Ricarica Dati", callback_data: 'dashboard-refresh' }
|
||||
]
|
||||
]
|
||||
}
|
||||
});
|
||||
} catch (e) { }
|
||||
}
|
||||
}
|
||||
];
|
||||
26
plugin/telegram/callbacks/data.js
Normal file
26
plugin/telegram/callbacks/data.js
Normal file
@@ -0,0 +1,26 @@
|
||||
module.exports = [
|
||||
{
|
||||
id: 'data-refresh',
|
||||
execute: async ({ bot, chatId, msg }) => {
|
||||
const dataCmd = require('../commands/data.js');
|
||||
const newText = dataCmd.formatSensorData();
|
||||
|
||||
try {
|
||||
await bot.editMessageText(newText, {
|
||||
chat_id: chatId,
|
||||
message_id: msg.message_id,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Aggiorna', callback_data: 'data-refresh' }]
|
||||
]
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (!e.message.includes('message is not modified')) {
|
||||
console.error('[Telegram Data] Errore refresh:', e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
141
plugin/telegram/callbacks/live.js
Normal file
141
plugin/telegram/callbacks/live.js
Normal file
@@ -0,0 +1,141 @@
|
||||
// Mappa globale per salvare gli interval id anche dopo un "hot-reload"
|
||||
if (!global.__meb_live_trackers) {
|
||||
global.__meb_live_trackers = new Map();
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
id: 'live-refresh',
|
||||
execute: async ({ bot, chatId, msg }) => {
|
||||
const liveCmd = require('../commands/live.js');
|
||||
const newText = liveCmd.formatLiveData();
|
||||
|
||||
try {
|
||||
await bot.editMessageText(newText, {
|
||||
chat_id: chatId,
|
||||
message_id: msg.message_id,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Avvia Live (2s)', callback_data: 'live-start' }],
|
||||
[{ text: 'Aggiorna', callback_data: 'live-refresh' }]
|
||||
]
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (!e.message.includes('message is not modified')) {
|
||||
console.error('[Telegram Live] Errore refresh:', e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'live-start',
|
||||
execute: async ({ bot, chatId, msg }) => {
|
||||
const liveCmd = require('../commands/live.js');
|
||||
|
||||
const messageId = msg.message_id;
|
||||
const liveKey = `${chatId}_${messageId}`;
|
||||
|
||||
// Se gia' attivo per questo messaggio, ignora
|
||||
if (global.__meb_live_trackers.has(liveKey)) return;
|
||||
|
||||
const stopMarkup = {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Ferma Live', callback_data: 'live-stop' }]
|
||||
]
|
||||
};
|
||||
|
||||
await bot.editMessageReplyMarkup(stopMarkup, {
|
||||
chat_id: chatId,
|
||||
message_id: messageId
|
||||
});
|
||||
|
||||
// 30 tick da 2 secondi = 60 secondi, poi auto-stop
|
||||
let count = 30;
|
||||
const intervalTimer = setInterval(async () => {
|
||||
count--;
|
||||
const baseText = liveCmd.formatLiveData();
|
||||
|
||||
// Auto-stop quando il tempo scade
|
||||
if (count <= 0) {
|
||||
if (global.__meb_live_trackers.has(liveKey)) {
|
||||
clearInterval(global.__meb_live_trackers.get(liveKey));
|
||||
global.__meb_live_trackers.delete(liveKey);
|
||||
}
|
||||
try {
|
||||
await bot.editMessageText(
|
||||
baseText + `\n_Live terminato automaticamente (60s)._`,
|
||||
{
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Avvia Live (2s)', callback_data: 'live-start' }],
|
||||
[{ text: 'Aggiorna', callback_data: 'live-refresh' }]
|
||||
]
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (e) { /* ignore */ }
|
||||
return;
|
||||
}
|
||||
|
||||
// Aggiornamento live con countdown
|
||||
const newText = baseText + `\n_Live attivo: arresto tra *${count * 2}s*_`;
|
||||
|
||||
try {
|
||||
await bot.editMessageText(newText, {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: stopMarkup
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.response && e.response.statusCode === 429) {
|
||||
console.warn('[Telegram Live] Rate limit raggiunto');
|
||||
} else if (e.message && e.message.includes('message to edit not found')) {
|
||||
// Messaggio cancellato dall'utente
|
||||
clearInterval(intervalTimer);
|
||||
global.__meb_live_trackers.delete(liveKey);
|
||||
}
|
||||
// Ignora "message is not modified"
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
global.__meb_live_trackers.set(liveKey, intervalTimer);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'live-stop',
|
||||
execute: async ({ bot, chatId, msg }) => {
|
||||
const liveCmd = require('../commands/live.js');
|
||||
|
||||
const messageId = msg.message_id;
|
||||
const liveKey = `${chatId}_${messageId}`;
|
||||
|
||||
// Pulisci l'interval se esiste
|
||||
if (global.__meb_live_trackers.has(liveKey)) {
|
||||
clearInterval(global.__meb_live_trackers.get(liveKey));
|
||||
global.__meb_live_trackers.delete(liveKey);
|
||||
}
|
||||
|
||||
const newText = liveCmd.formatLiveData();
|
||||
|
||||
try {
|
||||
await bot.editMessageText(newText + '\n_Live fermato._', {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Avvia Live (2s)', callback_data: 'live-start' }],
|
||||
[{ text: 'Aggiorna', callback_data: 'live-refresh' }]
|
||||
]
|
||||
}
|
||||
});
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
];
|
||||
51
plugin/telegram/callbacks/logs.js
Normal file
51
plugin/telegram/callbacks/logs.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const realtime = require('../../realtime/core.js');
|
||||
const { config } = require('../../config.js');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
id: 'logs-refresh',
|
||||
execute: async ({ bot, chatId, msg }) => {
|
||||
const stats = realtime.getStats();
|
||||
const consoleUrl = config.cloudUrl || 'https://console.mebboat.it';
|
||||
|
||||
let statusIcon = '🔴';
|
||||
if (stats.status === 'connected') statusIcon = '🟢';
|
||||
else if (stats.status === 'error') statusIcon = '🟡';
|
||||
|
||||
const now = new Date().toLocaleTimeString('it-IT');
|
||||
let text = `📊 *Registrazione Dati Realtime*\n\n`;
|
||||
text += `Stato: ${statusIcon} *${stats.status}*\n`;
|
||||
text += `Sensore: \`${stats.sensorID}\`\n`;
|
||||
text += `Messaggi inviati: *${stats.sent}*\n`;
|
||||
text += `Frequenza: ogni *${stats.sentEveryMLS / 1000}s*\n`;
|
||||
|
||||
if (stats.buffered > 0) {
|
||||
text += `⚠️ Messaggi in buffer: *${stats.buffered}*\n`;
|
||||
}
|
||||
|
||||
if (stats.reconnections > 0) {
|
||||
text += `Riconnessioni: ${stats.reconnections}\n`;
|
||||
}
|
||||
|
||||
text += `\n_(Aggiornato: ${now})_`;
|
||||
|
||||
try {
|
||||
await bot.editMessageText(text, {
|
||||
chat_id: chatId,
|
||||
message_id: msg.message_id,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: '📈 Apri Console Log', url: `${consoleUrl}/logs` }],
|
||||
[{ text: '🔄 Aggiorna Stato', callback_data: 'logs-refresh' }]
|
||||
]
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (!e.message.includes('message is not modified')) {
|
||||
console.error("[Telegram] Errore refresh logs:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
80
plugin/telegram/callbacks/settings.js
Normal file
80
plugin/telegram/callbacks/settings.js
Normal file
@@ -0,0 +1,80 @@
|
||||
module.exports = [
|
||||
{
|
||||
id: 'set-meteo',
|
||||
execute: async ({ bot, chatId, app }) => {
|
||||
const config = app.mebConfig;
|
||||
const currentFreqMin = config.forecast_current_frequency / 60000;
|
||||
const hourlyFreqMin = config.forecast_hourly_frequency / 60000;
|
||||
|
||||
const msg = `*Configura Aggiornamenti Meteo*\n\n` +
|
||||
`Aggiorno il meteo (attuale) ogni *${currentFreqMin} minuti*\n` +
|
||||
`Registro le previsioni future (prossimi 7 giorni) ogni *${hourlyFreqMin} minuti*`;
|
||||
|
||||
await bot.sendMessage(chatId, msg, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[
|
||||
{ text: "1 sec", callback_data: 'set-meteo-curr-1' },
|
||||
{ text: "10 sec", callback_data: 'set-meteo-curr-10' },
|
||||
],
|
||||
[
|
||||
{ text: "1 min", callback_data: 'set-meteo-curr-60' },
|
||||
{ text: "10 min", callback_data: 'set-meteo-curr-600' }
|
||||
],
|
||||
[
|
||||
{ text: "30m", callback_data: 'set-meteo-hour-1800' }
|
||||
],
|
||||
[
|
||||
{ text: "⬅️ Indietro", callback_data: 'session-refresh' }
|
||||
]
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
match: (data) => data.startsWith('set-meteo-curr-'),
|
||||
execute: async ({ bot, chatId, app, data, msg }) => {
|
||||
const val = parseInt(data.replace('set-meteo-curr-', ''), 10);
|
||||
if (app.mebPlugin && app.mebPlugin.setConfig) {
|
||||
app.mebPlugin.setConfig('forecast_current_frequency', val);
|
||||
await bot.editMessageText(`✅ Frequenza Aggiornamenti meteo aggiornata a *${val / 60} minuti*.\n_Ritorno al menu..._`, {
|
||||
chat_id: chatId,
|
||||
message_id: msg.message_id,
|
||||
parse_mode: 'Markdown'
|
||||
});
|
||||
setTimeout(() => {
|
||||
const sessionCmd = require('../commands/status.js');
|
||||
bot.editMessageText("*Servizi*\n\n", {
|
||||
chat_id: chatId, message_id: msg.message_id, parse_mode: 'Markdown', reply_markup: sessionCmd.createSessionMenu(app).reply_markup
|
||||
}).catch(() => { });
|
||||
}, 3000);
|
||||
} else {
|
||||
await bot.sendMessage(chatId, "Errore: il plugin non è accessibile per salvare la configurazione.");
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
match: (data) => data.startsWith('set-meteo-hour-'),
|
||||
execute: async ({ bot, chatId, app, data, msg }) => {
|
||||
const val = parseInt(data.replace('set-meteo-hour-', ''), 10);
|
||||
if (app.mebPlugin && app.mebPlugin.setConfig) {
|
||||
app.mebPlugin.setConfig('forecast_hourly_frequency', val);
|
||||
await bot.editMessageText(`✅ Frequenza previsioni future aggiornata a *${val / 60} minuti*.\n_Ritorno al menu..._`, {
|
||||
chat_id: chatId,
|
||||
message_id: msg.message_id,
|
||||
parse_mode: 'Markdown'
|
||||
});
|
||||
setTimeout(() => {
|
||||
const sessionCmd = require('../commands/status.js');
|
||||
bot.editMessageText("*Servizi*\n\n", {
|
||||
chat_id: chatId, message_id: msg.message_id, parse_mode: 'Markdown', reply_markup: sessionCmd.createSessionMenu(app).reply_markup
|
||||
}).catch(() => { });
|
||||
}, 3000);
|
||||
} else {
|
||||
await bot.sendMessage(chatId, "Errore: il plugin non è accessibile per salvare la configurazione.");
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
67
plugin/telegram/callbacks/status.js
Normal file
67
plugin/telegram/callbacks/status.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const realtime = require('../../realtime/core.js');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
id: 'session-weather-toggle',
|
||||
execute: async ({ bot, chatId, app, msg }) => {
|
||||
if (!app.mebPlugin) {
|
||||
return bot.answerCallbackQuery(msg.id, { text: "Errore: Plugin Meteo non caricato" });
|
||||
}
|
||||
|
||||
let isActive = app.mebPlugin.isPollingActive();
|
||||
|
||||
if (isActive) {
|
||||
app.mebPlugin.stopPolling();
|
||||
} else {
|
||||
app.mebPlugin.startPolling();
|
||||
}
|
||||
|
||||
const sessionCmd = require('../commands/status.js');
|
||||
const newMarkup = sessionCmd.createSessionMenu(app);
|
||||
|
||||
await bot.editMessageReplyMarkup(newMarkup.reply_markup, {
|
||||
chat_id: chatId,
|
||||
message_id: msg.message_id
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'session-realtime-info',
|
||||
execute: async ({ bot, chatId, msg }) => {
|
||||
const stats = realtime.getStats();
|
||||
|
||||
let text = `📡 *Stato Realtime*\n\n`;
|
||||
text += `Stato: *${stats.status}*\n`;
|
||||
text += `Sensore: \`${stats.sensorID}\`\n`;
|
||||
text += `Messaggi inviati: *${stats.sent}*\n`;
|
||||
text += `Buffer: ${stats.buffered} msg\n`;
|
||||
text += `Riconnessioni: ${stats.reconnections}\n`;
|
||||
text += `\n_I dati vengono inviati automaticamente ogni ${stats.sentEveryMLS / 1000}s_`;
|
||||
|
||||
await bot.answerCallbackQuery(msg.id, { text: `Realtime: ${stats.status} | ${stats.sent} msg inviati` });
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'session-refresh',
|
||||
execute: async ({ bot, chatId, app, msg }) => {
|
||||
const sessionCmd = require('../commands/status.js');
|
||||
const newMarkup = sessionCmd.createSessionMenu(app);
|
||||
|
||||
const now = new Date().toLocaleTimeString('it-IT');
|
||||
const newText = `*Servizi*\n\n_(Ultimo aggiornamento: ${now})_`;
|
||||
|
||||
try {
|
||||
await bot.editMessageText(newText, {
|
||||
chat_id: chatId,
|
||||
message_id: msg.message_id,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: newMarkup.reply_markup
|
||||
});
|
||||
} catch (e) {
|
||||
if (!e.message.includes('message is not modified')) {
|
||||
console.error("Errore nel refresh session:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
26
plugin/telegram/callbacks/weather.js
Normal file
26
plugin/telegram/callbacks/weather.js
Normal file
@@ -0,0 +1,26 @@
|
||||
module.exports = [
|
||||
{
|
||||
id: 'weather-refresh',
|
||||
execute: async ({ bot, chatId, msg }) => {
|
||||
const weather = require('../commands/weather.js');
|
||||
const newText = weather.formatWeatherData();
|
||||
|
||||
try {
|
||||
await bot.editMessageText(newText, {
|
||||
chat_id: chatId,
|
||||
message_id: msg.message_id,
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Aggiorna', callback_data: 'weather-refresh' }]
|
||||
]
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (!e.message.includes('message is not modified')) {
|
||||
console.error('[Telegram Weather] Errore refresh:', e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
57
plugin/telegram/commands/dashboard.js
Normal file
57
plugin/telegram/commands/dashboard.js
Normal file
@@ -0,0 +1,57 @@
|
||||
const dataHub = require('../../tools/dataHub');
|
||||
|
||||
function formatSensorData() {
|
||||
const sensorSnapshot = dataHub.getSensorData();
|
||||
const data = { timestamp: new Date().toISOString(), ...(sensorSnapshot || {}) };
|
||||
let output = `📊 *Dashboard Sensori*\n`;
|
||||
output += `_Ultimo aggiornamento: ${new Date().toLocaleTimeString('it-IT')}_\n\n`;
|
||||
|
||||
let isDataEmpty = true;
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (key === 'timestamp') continue;
|
||||
isDataEmpty = false;
|
||||
|
||||
let formattedKey = key.replace(/_/g, ' ');
|
||||
// Prima lettera maiuscola
|
||||
formattedKey = formattedKey.charAt(0).toUpperCase() + formattedKey.slice(1);
|
||||
|
||||
const formattedValue = (value !== null && value !== undefined)
|
||||
? (typeof value === 'number' ? value.toFixed(2) : value)
|
||||
: 'N/A';
|
||||
|
||||
output += `🔹 *${formattedKey}:* ${formattedValue}\n`;
|
||||
}
|
||||
|
||||
if (isDataEmpty) {
|
||||
output += `_Nessun dato configurato o letto. Controlla sensors.references.json_\n`;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
command: 'dashboard',
|
||||
description: 'Mostra i sensori live (dal file references)',
|
||||
pattern: /\/dashboard/,
|
||||
execute: async (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
|
||||
const text = formatSensorData();
|
||||
|
||||
await bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[
|
||||
{ text: "⚡️ Avvia Live Tracker (2s)", callback_data: 'dashboard-live-start' }
|
||||
],
|
||||
[
|
||||
{ text: "🔄 Ricarica Dati", callback_data: 'dashboard-refresh' }
|
||||
]
|
||||
]
|
||||
}
|
||||
});
|
||||
},
|
||||
formatSensorData // Esportato per riuso nel refresh e nel live
|
||||
};
|
||||
58
plugin/telegram/commands/data.js
Normal file
58
plugin/telegram/commands/data.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const dataHub = require('../../tools/dataHub');
|
||||
|
||||
/**
|
||||
* Formatta i dati sensore in un messaggio Telegram leggibile.
|
||||
* @returns {string} Testo formattato Markdown
|
||||
*/
|
||||
function formatSensorData() {
|
||||
const sensors = dataHub.getSensorData();
|
||||
|
||||
if (!sensors) {
|
||||
return 'Nessun dato sensore disponibile.\nI sensori potrebbero non essere ancora attivi.';
|
||||
}
|
||||
|
||||
let text = '*Dati Sensori*\n';
|
||||
text += `_${new Date().toLocaleTimeString('it-IT')}_\n\n`;
|
||||
|
||||
let hasData = false;
|
||||
|
||||
for (const [key, value] of Object.entries(sensors)) {
|
||||
if (key.startsWith('_')) continue; // Skip campi interni
|
||||
|
||||
hasData = true;
|
||||
let label = key.replace(/_/g, ' ');
|
||||
label = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
|
||||
const formatted = (value !== null && value !== undefined)
|
||||
? (typeof value === 'number' ? value.toFixed(2) : String(value))
|
||||
: 'N/A';
|
||||
|
||||
text += `*${label}:* ${formatted}\n`;
|
||||
}
|
||||
|
||||
if (!hasData) {
|
||||
text += '_Nessun dato configurato. Controlla sensors.references.json_\n';
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
command: 'data',
|
||||
description: 'Mostra i dati sensori attuali',
|
||||
pattern: /\/data/,
|
||||
execute: async (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const text = formatSensorData();
|
||||
|
||||
await bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Aggiorna', callback_data: 'data-refresh' }]
|
||||
]
|
||||
}
|
||||
});
|
||||
},
|
||||
formatSensorData
|
||||
};
|
||||
84
plugin/telegram/commands/live.js
Normal file
84
plugin/telegram/commands/live.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const dataHub = require('../../tools/dataHub');
|
||||
|
||||
/**
|
||||
* Formatta tutti i dati (sensori + meteo) per il live tracker.
|
||||
* @returns {string} Testo formattato Markdown
|
||||
*/
|
||||
function formatLiveData() {
|
||||
const sensors = dataHub.getSensorData();
|
||||
const { forecast, sea } = dataHub.getWeatherData();
|
||||
|
||||
let text = '*LIVE - Dati Completi*\n';
|
||||
text += `_${new Date().toLocaleTimeString('it-IT')}_\n\n`;
|
||||
|
||||
// Sezione sensori
|
||||
if (sensors) {
|
||||
text += '*Sensori:*\n';
|
||||
for (const [key, value] of Object.entries(sensors)) {
|
||||
if (key.startsWith('_')) continue;
|
||||
|
||||
let label = key.replace(/_/g, ' ');
|
||||
label = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
|
||||
const val = (value !== null && value !== undefined)
|
||||
? (typeof value === 'number' ? value.toFixed(2) : String(value))
|
||||
: 'N/A';
|
||||
|
||||
text += ` ${label}: ${val}\n`;
|
||||
}
|
||||
} else {
|
||||
text += '_Nessun dato sensore disponibile_\n';
|
||||
}
|
||||
|
||||
// Sezione meteo (compatta)
|
||||
if (forecast) {
|
||||
text += '\n*Meteo:*\n';
|
||||
const parts = [];
|
||||
if (forecast.temperature !== null && forecast.temperature !== undefined) {
|
||||
parts.push(`Temp: ${forecast.temperature}C`);
|
||||
}
|
||||
if (forecast.humidity !== null && forecast.humidity !== undefined) {
|
||||
parts.push(`Um: ${forecast.humidity}%`);
|
||||
}
|
||||
if (forecast.wind?.speed !== null && forecast.wind?.speed !== undefined) {
|
||||
parts.push(`Vento: ${forecast.wind.speed}km/h`);
|
||||
}
|
||||
text += ` ${parts.join(' | ')}\n`;
|
||||
}
|
||||
|
||||
if (sea?.waves) {
|
||||
const seaParts = [];
|
||||
if (sea.waves.height !== null && sea.waves.height !== undefined) {
|
||||
seaParts.push(`Onde: ${sea.waves.height}m`);
|
||||
}
|
||||
if (sea.waves.period !== null && sea.waves.period !== undefined) {
|
||||
seaParts.push(`Per: ${sea.waves.period}s`);
|
||||
}
|
||||
if (seaParts.length > 0) {
|
||||
text += ` ${seaParts.join(' | ')}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
command: 'live',
|
||||
description: 'Dati live (meteo + sensori) con aggiornamento automatico',
|
||||
pattern: /\/live/,
|
||||
execute: async (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const text = formatLiveData();
|
||||
|
||||
await bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Avvia Live (2s)', callback_data: 'live-start' }],
|
||||
[{ text: 'Aggiorna', callback_data: 'live-refresh' }]
|
||||
]
|
||||
}
|
||||
});
|
||||
},
|
||||
formatLiveData
|
||||
};
|
||||
53
plugin/telegram/commands/logs.js
Normal file
53
plugin/telegram/commands/logs.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const realtime = require('../../realtime/core.js');
|
||||
const { config } = require('../../config.js');
|
||||
|
||||
module.exports = {
|
||||
command: 'logs',
|
||||
description: 'Mostra lo stato della registrazione dati in tempo reale',
|
||||
pattern: /\/logs/,
|
||||
execute: async (bot, msg, { app }) => {
|
||||
const chatId = msg.chat.id;
|
||||
try {
|
||||
const stats = realtime.getStats();
|
||||
const consoleUrl = config.cloudUrl || 'https://console.mebboat.it';
|
||||
|
||||
let statusIcon = '🔴';
|
||||
if (stats.status === 'connected') statusIcon = '🟢';
|
||||
else if (stats.status === 'error') statusIcon = '🟡';
|
||||
|
||||
let text = `📊 *Registrazione Dati Realtime*\n\n`;
|
||||
text += `Stato: ${statusIcon} *${stats.status}*\n`;
|
||||
text += `Sensore: \`${stats.sensorID}\`\n`;
|
||||
text += `Messaggi inviati: *${stats.sent}*\n`;
|
||||
text += `Frequenza: ogni *${stats.sentEveryMLS / 1000}s*\n`;
|
||||
|
||||
if (stats.buffered > 0) {
|
||||
text += `⚠️ Messaggi in buffer: *${stats.buffered}*\n`;
|
||||
}
|
||||
|
||||
if (stats.reconnections > 0) {
|
||||
text += `Riconnessioni: ${stats.reconnections}\n`;
|
||||
}
|
||||
|
||||
if (stats.firstSent) {
|
||||
text += `\nPrimo invio: ${stats.firstSent}\n`;
|
||||
}
|
||||
|
||||
text += `\n_I dati vengono inviati automaticamente al server ogni secondo._`;
|
||||
text += `\n_Consulta i log storici sulla console:_`;
|
||||
|
||||
await bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: '📈 Apri Console Log', url: `${consoleUrl}/logs` }],
|
||||
[{ text: '🔄 Aggiorna Stato', callback_data: 'logs-refresh' }]
|
||||
]
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[Telegram] Errore comando /logs:", error);
|
||||
bot.sendMessage(chatId, `❌ Errore: ${error.message}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
24
plugin/telegram/commands/realtime.js
Normal file
24
plugin/telegram/commands/realtime.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const realtime = require('../../realtime/core.js');
|
||||
|
||||
module.exports = {
|
||||
command: 'realtime',
|
||||
description: 'Dettagli della connessione realtime',
|
||||
pattern: /\/realtime/,
|
||||
execute: async (bot, msg) => {
|
||||
const stats = realtime.getStats();
|
||||
const statusEmoji = stats.status === 'connected' ? '🟢' : '🔴';
|
||||
|
||||
let message = `*Connessione Realtime* ${statusEmoji}\n\n`;
|
||||
message += `*ID Sensore:* ${stats.sensorID}\n`;
|
||||
message += `*Stato:* ${stats.status}\n`;
|
||||
message += `*Messaggi inviati:* ${stats.sent}\n`;
|
||||
message += `*Riconnessioni:* ${stats.reconnections}\n`;
|
||||
message += `*Frequenza:* ${stats.sentEveryMLS}ms\n`;
|
||||
|
||||
if (stats.firstSent) {
|
||||
message += `*Primo invio:* ${new Date(stats.firstSent).toLocaleString()}\n`;
|
||||
}
|
||||
|
||||
await bot.sendMessage(msg.chat.id, message, { parse_mode: 'Markdown' });
|
||||
}
|
||||
};
|
||||
20
plugin/telegram/commands/settings.js
Normal file
20
plugin/telegram/commands/settings.js
Normal file
@@ -0,0 +1,20 @@
|
||||
module.exports = {
|
||||
command: 'settings',
|
||||
description: 'Mostra le impostazioni del Computer di Bordo',
|
||||
pattern: /\/settings/,
|
||||
execute: async (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
|
||||
await bot.sendMessage(chatId, "*Configurazione Computer di Bordo*\nScegli quali parametri modificare:", {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[
|
||||
{ text: "Meteo", callback_data: 'set-meteo' },
|
||||
{ text: "Batterie", callback_data: 'set-batteries' }
|
||||
]
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
40
plugin/telegram/commands/status.js
Normal file
40
plugin/telegram/commands/status.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const realtime = require('../../realtime/core.js');
|
||||
|
||||
function createSessionMenu(app) {
|
||||
const weatherActive = app.mebPlugin && app.mebPlugin.isPollingActive ? app.mebPlugin.isPollingActive() : false;
|
||||
const realtimeStats = realtime.getStats();
|
||||
const realtimeConnected = realtimeStats.isConnected;
|
||||
|
||||
return {
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[
|
||||
{ text: weatherActive ? "Meteo: 🟢 ON (Premi per fermare)" : "Meteo: 🔴 OFF (Premi per avviare)", callback_data: 'session-weather-toggle' }
|
||||
],
|
||||
[
|
||||
{ text: realtimeConnected ? "Realtime: 🟢 Connesso" : "Realtime: 🔴 Disconnesso", callback_data: 'session-realtime-info' }
|
||||
],
|
||||
[
|
||||
{ text: "🔄", callback_data: 'session-refresh' },
|
||||
{ text: "⚙️ ⛅️ (meteo)", callback_data: 'set-meteo' }
|
||||
]
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
command: 'session',
|
||||
description: 'Verifica le attività di Meteo e Realtime',
|
||||
pattern: /\/session/,
|
||||
execute: async (bot, msg, { app }) => {
|
||||
const chatId = msg.chat.id;
|
||||
const msgText = `*Servizi*\n\n`;
|
||||
|
||||
await bot.sendMessage(chatId, msgText, {
|
||||
parse_mode: 'Markdown',
|
||||
...createSessionMenu(app)
|
||||
});
|
||||
},
|
||||
createSessionMenu
|
||||
};
|
||||
47
plugin/telegram/commands/structure.js
Normal file
47
plugin/telegram/commands/structure.js
Normal file
@@ -0,0 +1,47 @@
|
||||
const realtime = require('../../realtime/core.js');
|
||||
|
||||
module.exports = {
|
||||
command: 'structure',
|
||||
description: 'Mostra la struttura dati del plugin',
|
||||
pattern: /\/structure/,
|
||||
execute: async (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const rules = realtime.getSensorRules();
|
||||
|
||||
if (!rules) {
|
||||
return bot.sendMessage(chatId, 'Nessuna configurazione sensori caricata.');
|
||||
}
|
||||
|
||||
let text = `*Struttura Dati Plugin*\n`;
|
||||
text += `Versione: \`${rules.version}\`\n`;
|
||||
text += `Attivo: ${rules.isActive ? 'Si' : 'No'}\n`;
|
||||
text += `Collezioni: ${rules.items?.length || 0}\n\n`;
|
||||
|
||||
if (rules.items) {
|
||||
for (const item of rules.items) {
|
||||
text += `*${item.collection}*\n`;
|
||||
text += ` Path: \`${item.main_path}\`\n`;
|
||||
|
||||
if (item.elements && Array.isArray(item.elements)) {
|
||||
for (const element of item.elements) {
|
||||
const { subelements, ...fields } = element;
|
||||
const [name, subPath] = Object.entries(fields)[0];
|
||||
text += ` - ${name} -> \`${item.main_path}.${subPath}\`\n`;
|
||||
|
||||
if (subelements && Array.isArray(subelements)) {
|
||||
for (const sub of subelements) {
|
||||
const [sName, sPath] = Object.entries(sub)[0];
|
||||
text += ` - ${sName} -> \`${item.main_path}.${subPath}.${sPath}\`\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
text += ` (valore singolo)\n`;
|
||||
}
|
||||
text += `\n`;
|
||||
}
|
||||
}
|
||||
|
||||
await bot.sendMessage(chatId, text, { parse_mode: 'Markdown' });
|
||||
}
|
||||
};
|
||||
84
plugin/telegram/commands/weather.js
Normal file
84
plugin/telegram/commands/weather.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const dataHub = require('../../tools/dataHub');
|
||||
|
||||
/**
|
||||
* Formatta i dati meteo in un messaggio Telegram leggibile.
|
||||
* @returns {string} Testo formattato Markdown
|
||||
*/
|
||||
function formatWeatherData() {
|
||||
const { forecast, sea } = dataHub.getWeatherData();
|
||||
|
||||
if (!forecast && !sea) {
|
||||
return 'Nessun dato meteo disponibile.\nIl polling potrebbe non essere ancora partito.';
|
||||
}
|
||||
|
||||
let text = '*Meteo Attuale*\n';
|
||||
text += `_${new Date().toLocaleTimeString('it-IT')}_\n\n`;
|
||||
|
||||
if (forecast) {
|
||||
if (forecast.temperature !== null && forecast.temperature !== undefined) {
|
||||
text += `Temperatura: *${forecast.temperature}*C\n`;
|
||||
}
|
||||
if (forecast.humidity !== null && forecast.humidity !== undefined) {
|
||||
text += `Umidita: *${forecast.humidity}*%\n`;
|
||||
}
|
||||
if (forecast.pressure !== null && forecast.pressure !== undefined) {
|
||||
text += `Pressione: *${forecast.pressure}* hPa\n`;
|
||||
}
|
||||
if (forecast.rain !== null && forecast.rain !== undefined) {
|
||||
text += `Pioggia: *${forecast.rain}* mm\n`;
|
||||
}
|
||||
|
||||
if (forecast.wind) {
|
||||
text += `\nVento:\n`;
|
||||
if (forecast.wind.speed !== null && forecast.wind.speed !== undefined) {
|
||||
text += ` Velocita: *${forecast.wind.speed}* km/h\n`;
|
||||
}
|
||||
if (forecast.wind.direction !== null && forecast.wind.direction !== undefined) {
|
||||
text += ` Direzione: *${forecast.wind.direction}*\n`;
|
||||
}
|
||||
if (forecast.wind.gusts !== null && forecast.wind.gusts !== undefined) {
|
||||
text += ` Raffiche: *${forecast.wind.gusts}* km/h\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sea) {
|
||||
text += `\nMare:\n`;
|
||||
if (sea.waves) {
|
||||
if (sea.waves.height !== null && sea.waves.height !== undefined) {
|
||||
text += ` Altezza onde: *${sea.waves.height}* m\n`;
|
||||
}
|
||||
if (sea.waves.period !== null && sea.waves.period !== undefined) {
|
||||
text += ` Periodo: *${sea.waves.period}* s\n`;
|
||||
}
|
||||
if (sea.waves.direction !== null && sea.waves.direction !== undefined) {
|
||||
text += ` Direzione: *${sea.waves.direction}*\n`;
|
||||
}
|
||||
}
|
||||
if (sea.temperature !== null && sea.temperature !== undefined) {
|
||||
text += ` Temp. acqua: *${sea.temperature}*C\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
command: 'weather',
|
||||
description: 'Mostra i dati meteo attuali',
|
||||
pattern: /\/weather/,
|
||||
execute: async (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const text = formatWeatherData();
|
||||
|
||||
await bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Aggiorna', callback_data: 'weather-refresh' }]
|
||||
]
|
||||
}
|
||||
});
|
||||
},
|
||||
formatWeatherData
|
||||
};
|
||||
269
plugin/telegram/telegram.core.js
Normal file
269
plugin/telegram/telegram.core.js
Normal file
@@ -0,0 +1,269 @@
|
||||
const TelegramBot = require('node-telegram-bot-api');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
|
||||
let bot = null;
|
||||
let app = null;
|
||||
let pollingRetryCount = 0;
|
||||
const MAX_POLLING_RETRIES = 10;
|
||||
const POLLING_BASE_DELAY_MS = 5000;
|
||||
|
||||
// Registry per i comandi, callback e query inline in formato { pattern: Regex, execute: Function }
|
||||
let commandsRegistry = [];
|
||||
let callbackHandlers = [];
|
||||
let inlineQueriesRegistry = [];
|
||||
let isMessageListenerRegistered = false;
|
||||
|
||||
// Inizializzazione del bot.
|
||||
function initBot() {
|
||||
if (!BOT_TOKEN) {
|
||||
console.warn("[Telegram] BOT_TOKEN not set: bot disabled");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (global.__meb_telegram_bot) {
|
||||
bot = global.__meb_telegram_bot;
|
||||
console.log("[Telegram] Già avviato. Riavvio del bot.");
|
||||
} else {
|
||||
bot = new TelegramBot(BOT_TOKEN, { polling: true });
|
||||
|
||||
// Gestione errori di polling: intercetta EFATAL (DNS/Rete) e riavvia con backoff esponenziale
|
||||
bot.on('polling_error', (error) => {
|
||||
const isNetworkError = error.code === 'EFATAL' || (error.message && (error.message.includes('EAI_AGAIN') || error.message.includes('ECONNREFUSED') || error.message.includes('ETIMEDOUT')));
|
||||
if (isNetworkError) {
|
||||
if (pollingRetryCount >= MAX_POLLING_RETRIES) {
|
||||
console.error(`[Telegram] Polling fallito dopo ${MAX_POLLING_RETRIES} tentativi. Bot disattivato. Riavviare il plugin per riprovare.`);
|
||||
return;
|
||||
}
|
||||
pollingRetryCount++;
|
||||
const delay = Math.min(POLLING_BASE_DELAY_MS * Math.pow(2, pollingRetryCount - 1), 300000); // max 5 min
|
||||
console.warn(`[Telegram] Errore Polling Critico (${error.code}), tentativo ${pollingRetryCount}/${MAX_POLLING_RETRIES}. Riavvio tra ${delay / 1000}s...`);
|
||||
setTimeout(() => {
|
||||
bot.startPolling({ restart: true })
|
||||
.then(() => { pollingRetryCount = 0; })
|
||||
.catch(err => console.error("[Telegram] Errore riavvio polling:", err.message));
|
||||
}, delay);
|
||||
} else {
|
||||
console.error(`[Telegram] Polling error: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
global.__meb_telegram_bot = bot;
|
||||
console.log("[Telegram] Avvio del bot.");
|
||||
}
|
||||
|
||||
// Caricamento dei comandi e dei callback.
|
||||
if (!global.__meb_telegram_handlers) {
|
||||
global.__meb_telegram_handlers = true;
|
||||
loadCommands();
|
||||
loadCallbacks();
|
||||
loadInlineQueries();
|
||||
setupMessageListener(); // Registra il listener generale dei messaggi
|
||||
}
|
||||
|
||||
return bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registra il listener centrale per tutti i messaggi.
|
||||
*/
|
||||
function setupMessageListener() {
|
||||
if (!bot || isMessageListenerRegistered) return;
|
||||
|
||||
bot.on('message', async (msg) => {
|
||||
if (!msg.text) return;
|
||||
// Cicla i comandi registrati e vedi se il testo corrisponde a un pattern
|
||||
for (const cmd of commandsRegistry) {
|
||||
if (cmd.pattern && cmd.pattern.test(msg.text)) {
|
||||
try {
|
||||
await cmd.execute(bot, msg, { app, getSK });
|
||||
} catch (error) {
|
||||
console.error(`[Telegram] Error executing command ${msg.text}:`, error);
|
||||
bot.sendMessage(msg.chat.id, "⚠️ Errore interno durante l'esecuzione del comando.");
|
||||
}
|
||||
return; // Trovato ed eseguito
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bot.on('callback_query', async (query) => {
|
||||
const chatId = query.message.chat.id;
|
||||
const data = query.data;
|
||||
|
||||
await bot.answerCallbackQuery(query.id);
|
||||
|
||||
const context = { bot, app, getSK, chatId, data, msg: query.message };
|
||||
|
||||
// Find matching handler
|
||||
const handler = callbackHandlers.find(h => {
|
||||
if (h.id) return h.id === data;
|
||||
if (h.match) return h.match(data);
|
||||
return false;
|
||||
});
|
||||
|
||||
if (handler) {
|
||||
try {
|
||||
await handler.execute(context);
|
||||
} catch (err) {
|
||||
const msgErr = err.message || (err.response && err.response.body && err.response.body.description) || String(err);
|
||||
if (msgErr.includes("message is not modified") || msgErr.includes("message to edit not found")) {
|
||||
// Silently ignore unmodified edit or deleted message
|
||||
} else {
|
||||
console.error(`[Telegram] Error executing callback ${data}:`, err);
|
||||
await bot.sendMessage(chatId, `Errore nella chimata dell'api, ${msgErr}.`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn(`[Telegram] Unknown callback action: ${data}`);
|
||||
await bot.sendMessage(chatId, `Azione sconosciuta: ${data}`);
|
||||
}
|
||||
});
|
||||
|
||||
bot.on('inline_query', async (query) => {
|
||||
const text = query.query;
|
||||
|
||||
// Cerca una query inline corrispondente
|
||||
for (const handler of inlineQueriesRegistry) {
|
||||
if (handler.pattern && handler.pattern.test(text)) {
|
||||
try {
|
||||
await handler.execute(bot, query, { app, getSK });
|
||||
} catch (err) {
|
||||
console.error(`[Telegram] Error executing inline query ${text}:`, err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
isMessageListenerRegistered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottiene il valore di una chiave dal DataBrowser di SignalK.
|
||||
* @param {*} skPath Nome della chiave (path completo, come ad esempio "navigation.position.latitude").
|
||||
* @returns Valore della chiave.
|
||||
*/
|
||||
function getSK(skPath) {
|
||||
if (!app) return null;
|
||||
const v = app.getSelfPath(skPath);
|
||||
return v && v.value !== undefined && v.value !== null ? v.value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Carica o ricarica i comandi del bot. Pulisce la cache di module_require per implementare l'hot reload.
|
||||
* @returns {void}
|
||||
*/
|
||||
function loadCommands() {
|
||||
if (!bot) return;
|
||||
const commandsDir = path.join(__dirname, 'commands');
|
||||
|
||||
if (fs.existsSync(commandsDir)) {
|
||||
commandsRegistry = []; // Svuota i vecchi comandi
|
||||
const menuCommands = []; // Per il menu di Telegram
|
||||
|
||||
// Legge solo i file .js dalla cartella /commands.
|
||||
const commandFiles = fs.readdirSync(commandsDir).filter(file => file.endsWith('.js'));
|
||||
// Per ogni file, importa il comando
|
||||
for (const file of commandFiles) {
|
||||
const fullPath = path.resolve(commandsDir, file);
|
||||
//Importa i comandi da module.exports all'interno del file
|
||||
const command = require(fullPath);
|
||||
|
||||
//Registra il comando nel registry interno.
|
||||
if (command.pattern && command.execute) {
|
||||
commandsRegistry.push(command);
|
||||
|
||||
// Se ha una descrizione e un nome comando, lo aggiungiamo al menu
|
||||
if (command.command && command.description) {
|
||||
menuCommands.push({
|
||||
command: command.command.toLowerCase(),
|
||||
description: command.description
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invia la lista dei comandi a Telegram per il menu a sinistra
|
||||
if (menuCommands.length > 0) {
|
||||
bot.setMyCommands(menuCommands).catch(err => {
|
||||
console.error("[Telegram] Errore nel setMyCommands:", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Carica o ricarica i callback del bot.
|
||||
* @returns {void}
|
||||
*/
|
||||
function loadCallbacks() {
|
||||
if (!bot) return;
|
||||
const callbacksDir = path.join(__dirname, 'callbacks');
|
||||
callbackHandlers = [];
|
||||
|
||||
if (fs.existsSync(callbacksDir)) {
|
||||
// Legge solo i file .js dalla cartella /callbacks.
|
||||
const callbackFiles = fs.readdirSync(callbacksDir).filter(file => file.endsWith('.js'));
|
||||
// Per ogni file, importa i callback e li aggiunge all'array callbackHandlers.
|
||||
for (const file of callbackFiles) {
|
||||
const fullPath = path.resolve(callbacksDir, file);
|
||||
//Importa i callback da module.exports all'interno del file
|
||||
const handlers = require(fullPath);
|
||||
if (Array.isArray(handlers)) {
|
||||
callbackHandlers.push(...handlers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Carica o ricarica le query inline del bot.
|
||||
* @returns {void}
|
||||
*/
|
||||
function loadInlineQueries() {
|
||||
if (!bot) return;
|
||||
const inlineDir = path.join(__dirname, 'inline');
|
||||
inlineQueriesRegistry = [];
|
||||
|
||||
if (fs.existsSync(inlineDir)) {
|
||||
const inlineFiles = fs.readdirSync(inlineDir).filter(file => file.endsWith('.js'));
|
||||
for (const file of inlineFiles) {
|
||||
const fullPath = path.resolve(inlineDir, file);
|
||||
const handler = require(fullPath);
|
||||
if (handler.pattern && handler.execute) {
|
||||
inlineQueriesRegistry.push(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Collega il bot all'app.
|
||||
* @param {*} mebApp L'app di SignalK.
|
||||
* @returns {TelegramBot} Il bot.
|
||||
*/
|
||||
function linkBotToApp(mebApp) {
|
||||
app = mebApp;
|
||||
bot = initBot();
|
||||
return bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invia un messaggio ad un utente tramite il bot.
|
||||
* @param {*} chatId L'ID della chat.
|
||||
* @param {*} text Il testo del messaggio.
|
||||
* @param {*} options Le opzioni del messaggio.
|
||||
* @returns {Promise<TelegramBot>} Il bot.
|
||||
*/
|
||||
function send(chatId, text, options = {}) {
|
||||
if (!bot) return Promise.reject("Bot not initialized");
|
||||
return bot.sendMessage(chatId, text, options);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
linkBotToApp,
|
||||
send
|
||||
};
|
||||
Reference in New Issue
Block a user