feat: Improve auto-scanner robustness and instructions
Provide clearer instructions for users on how to keep the automated scanner running 24/7 by suggesting external scheduling services. Refactor the server-side auto-scanner logic to use `runTransaction` for atomic updates of `lastAutoScan`, preventing potential race conditions and ensuring greater reliability.
This commit is contained in:
@@ -6,7 +6,7 @@ import * as OneSignal from 'onesignal-node';
|
|||||||
|
|
||||||
// Import Firebase (Server-side compatible since v9 supports Node environments)
|
// Import Firebase (Server-side compatible since v9 supports Node environments)
|
||||||
import { db } from "./src/lib/firebase";
|
import { db } from "./src/lib/firebase";
|
||||||
import { doc, getDoc, setDoc, addDoc, collection } from "firebase/firestore";
|
import { doc, getDoc, setDoc, addDoc, collection, runTransaction } from "firebase/firestore";
|
||||||
|
|
||||||
async function startServer() {
|
async function startServer() {
|
||||||
const app = express();
|
const app = express();
|
||||||
@@ -133,10 +133,15 @@ _Analisado por Inteligência Institucional (Worker Node)_
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
setInterval(async () => {
|
const runAutoScannerJob = async () => {
|
||||||
try {
|
try {
|
||||||
const settingsRef = doc(db, 'settings', 'app');
|
const settingsRef = doc(db, 'settings', 'app');
|
||||||
const settingsSnap = await getDoc(settingsRef);
|
let botToken = '';
|
||||||
|
let chatId = '';
|
||||||
|
let shouldExecute = false;
|
||||||
|
|
||||||
|
await runTransaction(db, async (transaction) => {
|
||||||
|
const settingsSnap = await transaction.get(settingsRef);
|
||||||
if (!settingsSnap.exists()) return;
|
if (!settingsSnap.exists()) return;
|
||||||
|
|
||||||
const data = settingsSnap.data();
|
const data = settingsSnap.data();
|
||||||
@@ -148,36 +153,49 @@ _Analisado por Inteligência Institucional (Worker Node)_
|
|||||||
|
|
||||||
// Check if enough time has passed based on interval
|
// Check if enough time has passed based on interval
|
||||||
if (now - lastScan >= intervalMins * 60 * 1000) {
|
if (now - lastScan >= intervalMins * 60 * 1000) {
|
||||||
console.log("[Worker] Executing Auto-Scanner...");
|
// Atomic lock: update lastAutoScan inside transaction
|
||||||
|
transaction.update(settingsRef, { lastAutoScan: now });
|
||||||
// Lock to prevent duplicates if multiple instances exist
|
shouldExecute = true;
|
||||||
await setDoc(settingsRef, { lastAutoScan: now }, { merge: true });
|
botToken = data.telegramBotToken || '';
|
||||||
|
chatId = data.telegramChatId || '';
|
||||||
const pairs = ['EURUSD', 'GBPUSD', 'BTCUSD', 'XAU/USD', 'US30'];
|
|
||||||
const randomPair = pairs[Math.floor(Math.random() * pairs.length)];
|
|
||||||
|
|
||||||
const decision = Math.random() > 0.5 ? 'BUY' : 'SELL';
|
|
||||||
const score = Math.floor(Math.random() * (99 - 70 + 1)) + 70;
|
|
||||||
|
|
||||||
const signalData = {
|
|
||||||
pair: randomPair,
|
|
||||||
decision,
|
|
||||||
timeframe: '15m - Modo Robô (Machine Learning Adaptativo)',
|
|
||||||
score,
|
|
||||||
entry: 'Preço Atual Mkt',
|
|
||||||
takeProfit: decision === 'BUY' ? '+25 pips' : '-25 pips',
|
|
||||||
stopLoss: decision === 'BUY' ? '-15 pips' : '+15 pips',
|
|
||||||
};
|
|
||||||
|
|
||||||
if (data.telegramBotToken && data.telegramChatId) {
|
|
||||||
await sendTelegramAlertServer(signalData, data.telegramBotToken, data.telegramChatId);
|
|
||||||
}
|
|
||||||
console.log(`[Worker] Sinais gerados e enviados para ${randomPair}`);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
});
|
||||||
console.error("[Worker] Erro no Auto-Scanner:", e);
|
|
||||||
}
|
if (shouldExecute) {
|
||||||
}, 60000); // Check every minute
|
console.log("[Worker] Executing Auto-Scanner...");
|
||||||
|
const pairs = ['EURUSD', 'GBPUSD', 'BTCUSD', 'XAU/USD', 'US30'];
|
||||||
|
const randomPair = pairs[Math.floor(Math.random() * pairs.length)];
|
||||||
|
|
||||||
|
const decision = Math.random() > 0.5 ? 'BUY' : 'SELL';
|
||||||
|
const score = Math.floor(Math.random() * (99 - 70 + 1)) + 70;
|
||||||
|
|
||||||
|
const signalData = {
|
||||||
|
pair: randomPair,
|
||||||
|
decision,
|
||||||
|
timeframe: '15m - Modo Robô (Machine Learning Adaptativo)',
|
||||||
|
score,
|
||||||
|
entry: 'Preço Atual Mkt',
|
||||||
|
takeProfit: decision === 'BUY' ? '+25 pips' : '-25 pips',
|
||||||
|
stopLoss: decision === 'BUY' ? '-15 pips' : '+15 pips',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (botToken && chatId) {
|
||||||
|
await sendTelegramAlertServer(signalData, botToken, chatId);
|
||||||
|
}
|
||||||
|
console.log(`[Worker] Sinais gerados e enviados para ${randomPair}`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[Worker] Erro no Auto-Scanner:", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setInterval(runAutoScannerJob, 60000); // Check every minute internally
|
||||||
|
|
||||||
|
app.get('/api/cron/auto-scanner', async (req, res) => {
|
||||||
|
// This endpoint can be pinged by UptimeRobot or Cron-job.org to prevent container sleep
|
||||||
|
await runAutoScannerJob();
|
||||||
|
res.json({ status: "ok", message: "Scanner check executed", timestamp: new Date().toISOString() });
|
||||||
|
});
|
||||||
|
|
||||||
app.listen(PORT, "0.0.0.0", () => {
|
app.listen(PORT, "0.0.0.0", () => {
|
||||||
console.log('Server running on http://localhost:' + PORT);
|
console.log('Server running on http://localhost:' + PORT);
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ export const AdminDashboard: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[10px] text-brand-red font-bold">
|
<p className="text-[10px] text-brand-red font-bold">
|
||||||
Atenção: Mantenha esta aba aberta para que o scanner automatizado (Robô Funcional no Browser) permaneça operando. Em um ambiente de produção V2, isto deve ser migrado para o lado do servidor (Cron Job/Worker Node).
|
Atenção: A aplicação entra em repouso quando não há abas abertas. Para o Robô funcionar 24/7 de forma totalmente automática, utilize um serviço gratuito como o <strong>cron-job.org</strong> ou <strong>UptimeRobot</strong> para visitar a URL <code>{window.location.origin}/api/cron/auto-scanner</code> a cada 1 minuto.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user