feat(telegram): add /belo command with current quote and daily progress
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
import { Bot } from "grammy";
|
||||
import {
|
||||
formatPrice,
|
||||
formatTime,
|
||||
getBeloQuoteStatus,
|
||||
isChatValidated,
|
||||
validateCode,
|
||||
} from "../modules/telegram/telegram.service";
|
||||
import { logger } from "./logger";
|
||||
import { validateCode } from "../modules/telegram/telegram.service";
|
||||
|
||||
const token = process.env.BOT_TOKEN;
|
||||
|
||||
@@ -30,6 +36,38 @@ export async function startBot(): Promise<void> {
|
||||
);
|
||||
});
|
||||
|
||||
bot.command("belo", async (ctx) => {
|
||||
const validated = await isChatValidated(BigInt(ctx.chat.id));
|
||||
if (!validated) {
|
||||
await ctx.reply(
|
||||
"🔒 Tu cuenta no está conectada. Enviá /start para ver cómo conectarla.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const status = await getBeloQuoteStatus();
|
||||
if (!status) {
|
||||
await ctx.reply("❌ No hay datos de BELO disponibles todavía.");
|
||||
return;
|
||||
}
|
||||
|
||||
const progress = Math.round(status.progressPercentage ?? 0);
|
||||
const message = [
|
||||
"💰 <b>BELO · USDC/ARS</b>",
|
||||
"",
|
||||
`Compra: <b>$${formatPrice(status.buy)}</b>`,
|
||||
`Venta: <b>$${formatPrice(status.sell)}</b>`,
|
||||
"",
|
||||
`Mínimo del día: $${formatPrice(status.minBuy)}`,
|
||||
`Máximo del día: $${formatPrice(status.maxBuy)}`,
|
||||
`Avance del día: <b>${progress}%</b>`,
|
||||
"",
|
||||
`🕐 ${formatTime(status.timeStamp)}`,
|
||||
].join("\n");
|
||||
|
||||
await ctx.reply(message, { parse_mode: "HTML" });
|
||||
});
|
||||
|
||||
bot.on("message:text", async (ctx) => {
|
||||
const text = ctx.message.text.trim();
|
||||
if (text.startsWith("/")) return;
|
||||
|
||||
@@ -13,7 +13,7 @@ function generateRandomCode(): string {
|
||||
return code;
|
||||
}
|
||||
|
||||
function formatPrice(price: number): string {
|
||||
export function formatPrice(price: number): string {
|
||||
return price.toLocaleString("es-AR", {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
@@ -72,7 +72,7 @@ export async function getTelegramStatus(): Promise<{
|
||||
};
|
||||
}
|
||||
|
||||
function formatTime(date: Date): string {
|
||||
export function formatTime(date: Date): string {
|
||||
const artOffset = -3;
|
||||
const artMs = date.getTime() + artOffset * 3600000;
|
||||
const artDate = new Date(artMs);
|
||||
@@ -208,3 +208,75 @@ export async function checkAndNotifyBelo(): Promise<void> {
|
||||
todayStart,
|
||||
});
|
||||
}
|
||||
|
||||
export interface BeloQuoteStatus {
|
||||
buy: number;
|
||||
sell: number;
|
||||
timeStamp: Date;
|
||||
minBuy: number;
|
||||
maxBuy: number;
|
||||
minSell: number;
|
||||
maxSell: number;
|
||||
progressPercentage: number | null;
|
||||
}
|
||||
|
||||
export async function getBeloQuoteStatus(): Promise<BeloQuoteStatus | null> {
|
||||
const currentQuote = await prisma.quote.findFirst({
|
||||
where: { type: "BELO" },
|
||||
orderBy: { timeStamp: "desc" },
|
||||
});
|
||||
if (!currentQuote) return null;
|
||||
|
||||
const now = new Date();
|
||||
const artOffset = -3;
|
||||
const artMs = now.getTime() + artOffset * 3600000;
|
||||
const artDate = new Date(artMs);
|
||||
const todayStart = new Date(
|
||||
Date.UTC(
|
||||
artDate.getUTCFullYear(),
|
||||
artDate.getUTCMonth(),
|
||||
artDate.getUTCDate(),
|
||||
),
|
||||
);
|
||||
|
||||
const agg = await prisma.quoteHistory.aggregate({
|
||||
where: {
|
||||
type: "BELO",
|
||||
timeStamp: { gte: todayStart },
|
||||
},
|
||||
_min: { buy: true, sell: true },
|
||||
_max: { buy: true, sell: true },
|
||||
});
|
||||
|
||||
const currentBuy = Number(currentQuote.buy);
|
||||
const minBuy = Number(agg._min.buy ?? currentQuote.buy);
|
||||
const maxBuy = Number(agg._max.buy ?? currentQuote.buy);
|
||||
|
||||
let progressPercentage: number | null = null;
|
||||
if (maxBuy > minBuy) {
|
||||
progressPercentage = Math.min(
|
||||
100,
|
||||
Math.max(0, ((currentBuy - minBuy) / (maxBuy - minBuy)) * 100),
|
||||
);
|
||||
} else {
|
||||
progressPercentage = 100;
|
||||
}
|
||||
|
||||
return {
|
||||
buy: currentBuy,
|
||||
sell: Number(currentQuote.sell),
|
||||
timeStamp: currentQuote.timeStamp,
|
||||
minBuy,
|
||||
maxBuy,
|
||||
minSell: Number(agg._min.sell ?? currentQuote.sell),
|
||||
maxSell: Number(agg._max.sell ?? currentQuote.sell),
|
||||
progressPercentage,
|
||||
};
|
||||
}
|
||||
|
||||
export async function isChatValidated(chatId: bigint): Promise<boolean> {
|
||||
const chat = await prisma.telegramChat.findFirst({
|
||||
where: { chatId, validatedAt: { not: null } },
|
||||
});
|
||||
return chat !== null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user