diff --git a/main.js b/main.js index 141ef53..64040bf 100644 --- a/main.js +++ b/main.js @@ -3,6 +3,14 @@ const { Client, GatewayIntentBits, EmbedBuilder } = require('discord.js'); const fs = require('fs'); const path = require('path'); +let priceHistory = { + currentPrice: 0, + prices: [{ + time: Date.now(), + price: 0, + }] +}; + const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages], }); @@ -15,10 +23,13 @@ let lastPriceMessageId; client.once('ready', async () => { console.log('Bot is online!'); - immediatePriceCheckAndAnnounce() - const solanaData = readSolanaData(); - lastKnownPriceAtStartup = solanaData ? solanaData.price : null; - lastPriceMessageId = solanaData ? solanaData.lastPriceMessageId : null; + let solanaData = readSolanaData(); + if (solanaData) { + priceHistory = solanaData; + lastKnownPriceAtStartup = solanaData.currentPrice; + lastPriceMessageId = solanaData.lastPriceMessageId; + } + immediatePriceCheckAndAnnounce(); checkPriceContinuously(); }); @@ -35,33 +46,110 @@ async function fetchSolanaPrice() { } } -async function immediatePriceCheckAndAnnounce() { +async function fetchSolanaPriceAndUpdateHistory() { + const currentPrice = await fetchSolanaPrice(); + if (currentPrice) { + + priceHistory.prices.push({ time: Date.now(), price: currentPrice }); + priceHistory.currentPrice = currentPrice; + + if (priceHistory.prices.length > 61) { + priceHistory.prices.shift(); + } + + saveSolanaData(priceHistory); + } +} + +function immediatePriceCheckAndAnnounce() { const solanaData = readSolanaData(); const lastKnownPrice = solanaData ? solanaData.price : null; - const currentPrice = await fetchSolanaPrice(); - - checkPriceContinuously(); + const currentPrice = fetchSolanaPrice(); +} + + + + const now = Date.now(); + + function calculateChanges() { + const latestPrice = parseFloat(priceHistory.currentPrice); + let oneMinChange = { percent: 0, dollar: 0 }; + let fiveMinChange = { percent: 0, dollar: 0 }; + let oneHourChange = { percent: 0, dollar: 0 }; + let oneDayChange = { percent: 0, dollar: 0 }; + + const now = Date.now(); + + function findPriceAgo(minutesAgo) { + const targetTime = now - minutesAgo * 60 * 1000; + return priceHistory.prices.reduce((prev, curr) => { + return Math.abs(curr.time - targetTime) < Math.abs(prev.time - targetTime) ? curr : prev; + }, priceHistory.prices[0]); + } + + if (priceHistory.prices.length >= 2) { + const oneMinAgoPrice = parseFloat(findPriceAgo(1).price); + oneMinChange.percent = ((latestPrice - oneMinAgoPrice) / oneMinAgoPrice) * 100; + oneMinChange.dollar = latestPrice - oneMinAgoPrice; + } + + if (priceHistory.prices.length >= 6) { + const fiveMinAgoPrice = parseFloat(findPriceAgo(5).price); + fiveMinChange.percent = ((latestPrice - fiveMinAgoPrice) / fiveMinAgoPrice) * 100; + fiveMinChange.dollar = latestPrice - fiveMinAgoPrice; + } + + if (priceHistory.prices.length >= 61) { + const oneHourAgoPrice = parseFloat(findPriceAgo(60).price); + oneHourChange.percent = ((latestPrice - oneHourAgoPrice) / oneHourAgoPrice) * 100; + oneHourChange.dollar = latestPrice - oneHourAgoPrice; + } + + if (priceHistory.prices.length >= 1440) { + const oneDayAgoPrice = parseFloat(findPriceAgo(1440).price); + oneDayChange.percent = ((latestPrice - oneDayAgoPrice) / oneDayAgoPrice) * 100; + oneDayChange.dollar = latestPrice - oneDayAgoPrice; + } + + return { oneMinChange, fiveMinChange, oneHourChange, oneDayChange }; +} + +const { oneMinChange, fiveMinChange, oneHourChange, oneDayChange } = calculateChanges(); + +async function sendNewPriceMessage(embed) { + const sentMessage = await solanaPriceChannel.send({ embeds: [embed] }); + lastPriceMessageId = sentMessage.id; + + saveSolanaData({ ...priceHistory, lastPriceMessageId: sentMessage.id }); +} + +async function sendNewPriceMessage(solanaPriceChannel, embed) { + const sentMessage = await solanaPriceChannel.send({ embeds: [embed] }); + lastPriceMessageId = sentMessage.id; + + saveSolanaData({ ...priceHistory, lastPriceMessageId: sentMessage.id }); } async function checkPriceContinuously() { - const price = await fetchSolanaPrice(); - if (!price) { - console.log('Could not fetch price, will try again in 60 seconds.'); - setTimeout(checkPriceContinuously, 60000); - return; - } - - console.log(`Current Solana Price: $${price}`); - const solanaPriceChannel = await client.channels.fetch(solanaPriceChannelId); + await fetchSolanaPriceAndUpdateHistory(); + const { oneMinChange, fiveMinChange, oneHourChange } = calculateChanges(); const embed = new EmbedBuilder() - .setColor(0x0099ff) - .setThumbnail('https://solana.com/src/img/branding/solanaLogoMark.png') - .setTitle('Solana (SOL) Price Update') - .setDescription(`**Current Price: \`$${price}\`**`) - .addFields({ name: '💰 Current Price', value: `**\`$${price}\`**`, inline: false }) - .setTimestamp() - .setImage(process.env.IMAGE_URL) + .setColor(0x0099ff) + .setThumbnail('https://solana.com/src/img/branding/solanaLogoMark.png') + .setTitle('Solana (SOL) Price Update') + .setDescription(`**Current Price: \`$${priceHistory.currentPrice}\`**`) + .addFields([ + { name: '💰 Current Price', value: `**\`$${priceHistory.currentPrice}\`**`, inline: false }, + { name: '1 Minute Change', value: `${oneMinChange.percent.toFixed(2)}% (${oneMinChange.dollar.toFixed(2)} USD)`, inline: true }, + { name: '5 Minute Change', value: `${fiveMinChange.percent.toFixed(2)}% (${fiveMinChange.dollar.toFixed(2)} USD)`, inline: true }, + { name: '1 Hour Change', value: `${oneHourChange.percent.toFixed(2)}% (${oneHourChange.dollar.toFixed(2)} USD)`, inline: true }, + { name: '1 Day Change', value: `${oneDayChange.percent.toFixed(2)}% (${oneDayChange.dollar.toFixed(2)} USD)`, inline: true } + ]) + .setTimestamp() + .setImage(process.env.IMAGE_URL); + + const solanaPriceChannel = await client.channels.fetch(solanaPriceChannelId); if (lastPriceMessageId) { try { @@ -69,22 +157,19 @@ async function checkPriceContinuously() { await message.edit({ embeds: [embed] }); } catch (error) { console.error('Error updating price message, sending a new one:', error); - const sentMessage = await solanaPriceChannel.send({ embeds: [embed] }); - lastPriceMessageId = sentMessage.id; + sendNewPriceMessage(solanaPriceChannel, embed); } } else { - const sentMessage = await solanaPriceChannel.send({ embeds: [embed] }); - lastPriceMessageId = sentMessage.id; + console.log('No lastPriceMessageId found, sending a new message.'); + sendNewPriceMessage(solanaPriceChannel, embed); } - if (lastKnownPriceAtStartup !== null && (parseFloat(price) - lastKnownPriceAtStartup >= 2.5)) { + if (lastKnownPriceAtStartup !== null && (parseFloat(priceHistory.currentPrice) - lastKnownPriceAtStartup >= 2.5)) { const announcementsChannel = await client.channels.fetch(announcementsChannelId); - await announcementsChannel.send(`@everyone Solana price has increased significantly! Current price: $${price}`); - lastKnownPriceAtStartup = parseFloat(price); + await announcementsChannel.send(`@everyone Solana price has increased significantly! Current price: $${priceHistory.currentPrice}`); + lastKnownPriceAtStartup = parseFloat(priceHistory.currentPrice); } - saveSolanaData({ price: parseFloat(price), lastPriceMessageId }); - setTimeout(checkPriceContinuously, 60000); } @@ -93,7 +178,8 @@ function saveSolanaData(data) { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } - fs.writeFileSync(solanaDataFile, JSON.stringify(data), 'utf8'); + + fs.writeFileSync(solanaDataFile, JSON.stringify(data, null, 2), 'utf8'); } function readSolanaData() {