polysignal_hackaton / backend /src /alerts /alerts.service.js
blackmistcode's picture
Add files using upload-large-folder tool
dfe11f8 verified
Raw
History Blame Contribute Delete
2.35 kB
/**
* Logica de negocio del modulo de alertas.
*
* Responsabilidades:
* - list(userId, query) → devuelve alertas paginadas del usuario.
* - processAll() → revisa la watchlist cada 60s (scheduler):
* 1. Obtiene entradas de watchlist con alertThreshold.
* 2. Si yesPrice >= alertThreshold y no hay alerta reciente (5 min dedup):
* - Crea alerta en base de datos.
* - Envía mensaje por Telegram (si hay bot token y chatId).
* - Emite evento 'price_alert' por Socket.io.
*
* Tipos de alerta:
* - price_threshold : umbral de precio cruzado.
* - signal_change : cambio de senal IA (reservado para futuras versiones).
*
* Consumido por:
* - alerts.controller.js (API REST).
* - scheduler.js (processAlerts cada 60s).
*/
import { z } from 'zod';
import { alertsRepository } from './alerts.repository.js';
import { watchlistRepository } from '../watchlist/watchlist.repository.js';
import { sendMessage, formatPriceAlert } from './telegram.client.js';
import { emitPriceAlert } from '../socket/broadcaster.js';
import { logger } from '../utils/logger.js';
const listQuery = z.object({
limit: z.coerce.number().int().min(1).max(200).default(50),
offset: z.coerce.number().int().min(0).default(0),
});
const DEDUP_WINDOW_MS = 5 * 60 * 1000; // 5 min
export const alertsService = {
list(userId, query) {
const { limit, offset } = listQuery.parse(query);
return alertsRepository.findByUser(userId, { limit, offset });
},
async processAll() {
const entries = await watchlistRepository.findAllWithThreshold();
for (const entry of entries) {
const { alertThreshold, user, market } = entry;
if (!market.yesPrice || market.yesPrice < alertThreshold) continue;
const recent = await alertsRepository.findRecent(user.id, market.id, 'price_threshold', DEDUP_WINDOW_MS);
if (recent) continue;
const message = formatPriceAlert(market.question, market.yesPrice, alertThreshold);
await alertsRepository.create({ userId: user.id, marketId: market.id, type: 'price_threshold', message });
await sendMessage(user.telegramChatId, message);
emitPriceAlert({ marketId: market.id, type: 'price_threshold', message });
logger.info({ marketId: market.id, userId: user.id }, 'price alert sent');
}
},
};