Updated Telegram notification

This commit is contained in:
Jose Selesan
2026-08-05 13:56:52 -03:00
parent 4784242a35
commit 0407a79e1e
4 changed files with 143 additions and 52 deletions

View File

@@ -0,0 +1,26 @@
-- CreateEnum
CREATE TYPE "PriceDirection" AS ENUM ('MAX', 'MIN');
-- CreateTable
CREATE TABLE "telegram_daily_price_notifications" (
"id" TEXT NOT NULL,
"date" TIMESTAMP(3) NOT NULL,
"quoteType" "QuoteType" NOT NULL,
"direction" "PriceDirection" NOT NULL,
"value" MONEY NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "telegram_daily_price_notifications_pkey" PRIMARY KEY ("id")
);
-- Migrate existing max records (if any)
INSERT INTO "telegram_daily_price_notifications" ("id", "date", "quoteType", "direction", "value", "createdAt", "updatedAt")
SELECT "id", "date", "quoteType", 'MAX'::"PriceDirection", "maxValue", "createdAt", "updatedAt"
FROM "telegram_daily_max_notifications";
-- CreateIndex
CREATE UNIQUE INDEX "telegram_daily_price_notifications_date_quoteType_direction_key" ON "telegram_daily_price_notifications"("date", "quoteType", "direction");
-- DropTable
DROP TABLE "telegram_daily_max_notifications";

View File

@@ -79,16 +79,22 @@ model TelegramChat {
@@map("telegram_chats")
}
model TelegramDailyMaxNotification {
id String @id @default(cuid())
enum PriceDirection {
MAX
MIN
}
model TelegramDailyPriceNotification {
id String @id @default(cuid())
date DateTime
quoteType QuoteType
maxValue Decimal @db.Money
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
direction PriceDirection
value Decimal @db.Money
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([date, quoteType])
@@map("telegram_daily_max_notifications")
@@unique([date, quoteType, direction])
@@map("telegram_daily_price_notifications")
}
enum QuoteType {

View File

@@ -1,7 +1,7 @@
import cron from "node-cron";
import { eventBus } from "../../lib/event-bus";
import { logger } from "../../lib/logger";
import { checkAndNotifyBeloMax } from "../telegram/telegram.service";
import { checkAndNotifyBelo } from "../telegram/telegram.service";
import { processBeloQuote } from "./belo.service";
import { processBlueQuote } from "./blue.service";
import { processBnaQuote } from "./bna.service";
@@ -14,7 +14,7 @@ async function safeProcess(
await fn();
logger.info(`${name} quote processed successfully`);
if (name === "BELO") {
await checkAndNotifyBeloMax();
await checkAndNotifyBelo();
}
} catch (error) {
logger.error(error, `Error processing ${name} quote`);

View File

@@ -1,6 +1,6 @@
import crypto from "crypto";
import { prisma } from "../../lib/prisma";
import { logger } from "../../lib/logger";
import { prisma } from "../../lib/prisma";
import { getBot, getBotUsername } from "../../lib/telegram";
function generateRandomCode(): string {
@@ -72,7 +72,93 @@ export async function getTelegramStatus(): Promise<{
};
}
export async function checkAndNotifyBeloMax(): Promise<void> {
function formatTime(date: Date): string {
const artOffset = -3;
const artMs = date.getTime() + artOffset * 3600000;
const artDate = new Date(artMs);
return artDate.toLocaleTimeString("es-AR", {
hour: "2-digit",
minute: "2-digit",
hour12: false,
timeZone: "UTC",
});
}
async function checkAndNotifyBound({
chatId,
direction,
currentValue,
quoteTime,
todayStart,
}: {
chatId: string | bigint;
direction: "MAX" | "MIN";
currentValue: number;
quoteTime: Date;
todayStart: Date;
}): Promise<void> {
const bot = getBot();
if (!bot) return;
const isMax = direction === "MAX";
const lastNotified = await prisma.telegramDailyPriceNotification.findUnique({
where: {
date_quoteType_direction: {
date: todayStart,
quoteType: "BELO",
direction,
},
},
});
const lastNotifiedValue = lastNotified
? Number(lastNotified.value)
: isMax
? 0
: Number.POSITIVE_INFINITY;
const shouldNotify = isMax
? currentValue > lastNotifiedValue
: currentValue < lastNotifiedValue;
if (!shouldNotify) return;
const formattedPrice = formatPrice(currentValue);
const time = formatTime(quoteTime);
const icon = isMax ? "🟢📈" : "🔴📉";
const word = isMax ? "máximo" : "mínimo";
const message = `${icon} BELO alcanzó el ${word} del día a las ${time}, cotización actual es de $${formattedPrice}`;
try {
await bot.api.sendMessage(Number(chatId), message);
await prisma.telegramDailyPriceNotification.upsert({
where: {
date_quoteType_direction: {
date: todayStart,
quoteType: "BELO",
direction,
},
},
create: {
date: todayStart,
quoteType: "BELO",
direction,
value: currentValue,
},
update: {
value: currentValue,
},
});
logger.info(`Telegram BELO ${word} notification sent: $${formattedPrice}`);
} catch (error) {
logger.error(error, `Failed to send Telegram BELO ${word} notification`);
}
}
export async function checkAndNotifyBelo(): Promise<void> {
const chat = await prisma.telegramChat.findFirst({
where: { validatedAt: { not: null } },
orderBy: { validatedAt: "desc" },
@@ -84,7 +170,7 @@ export async function checkAndNotifyBeloMax(): Promise<void> {
if (!bot) return;
const now = new Date();
const artHour = ((now.getUTCHours() - 3) % 24 + 24) % 24;
const artHour = (((now.getUTCHours() - 3) % 24) + 24) % 24;
if (artHour < 8 || artHour >= 20) return;
const currentQuote = await prisma.quote.findFirst({
@@ -106,46 +192,19 @@ export async function checkAndNotifyBeloMax(): Promise<void> {
),
);
const lastNotified = await prisma.telegramDailyMaxNotification.findUnique({
where: {
date_quoteType: {
date: todayStart,
quoteType: "BELO",
},
},
await checkAndNotifyBound({
chatId: chat.chatId,
direction: "MAX",
currentValue: currentBuy,
quoteTime: currentQuote.timeStamp,
todayStart,
});
const lastNotifiedValue = lastNotified ? Number(lastNotified.maxValue) : 0;
if (currentBuy <= lastNotifiedValue) return;
const formattedPrice = formatPrice(currentBuy);
const message = `BELO alcanzó el máximo del día, la cotización actual es de $${formattedPrice}`;
try {
await bot.api.sendMessage(Number(chat.chatId), message);
await prisma.telegramDailyMaxNotification.upsert({
where: {
date_quoteType: {
date: todayStart,
quoteType: "BELO",
},
},
create: {
date: todayStart,
quoteType: "BELO",
maxValue: currentBuy,
},
update: {
maxValue: currentBuy,
},
});
logger.info(
`Telegram BELO max notification sent: $${formattedPrice}`,
);
} catch (error) {
logger.error(error, "Failed to send Telegram BELO max notification");
}
await checkAndNotifyBound({
chatId: chat.chatId,
direction: "MIN",
currentValue: currentBuy,
quoteTime: currentQuote.timeStamp,
todayStart,
});
}