require('dotenv').config(); 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], }); const solanaPriceChannelId = process.env.SOLANA_PRICE_CHANNEL_ID; const announcementsChannelId = process.env.ANNOUNCEMENTS_CHANNEL_ID; const solanaDataFile = './data/solana.json'; let lastAnnouncedPrice; let lastPriceMessageId; client.once('ready', async () => { console.log('Bot is online!'); let solanaData = readSolanaData(); if (solanaData) { priceHistory = solanaData; lastAnnouncedPrice = solanaData.lastAnnouncedPrice; lastPriceMessageId = solanaData.lastPriceMessageId; } immediatePriceCheckAndAnnounce(); checkPriceContinuously(); }); async function fetchSolanaPrice() { const cryptocompareApiUrl = 'https://min-api.cryptocompare.com/data/price?fsym=SOL&tsyms=USD'; try { const fetch = (await import('node-fetch')).default; const response = await fetch(cryptocompareApiUrl); const data = await response.json(); return parseFloat(data.USD).toFixed(2); } catch (error) { console.error('Error fetching Solana price:', error); return null; } } function pruneOldData() { const sixHoursInMilliseconds = 6 * 60 * 60 * 1000; const oneDayInMilliseconds = 24 * 60 * 60 * 1000; const cutoffTime6Hours = Date.now() - sixHoursInMilliseconds; const cutoffTime1Day = Date.now() - oneDayInMilliseconds; priceHistory.prices = priceHistory.prices.filter(pricePoint => pricePoint.time > cutoffTime1Day); if (priceHistory.prices.length < 360) { console.warn("Warning: Not enough data points for accurate 6-hour calculations."); } } async function fetchSolanaPriceAndUpdateHistory() { const currentPrice = await fetchSolanaPrice(); if (currentPrice) { pruneOldData(); priceHistory.prices.push({ time: Date.now(), price: currentPrice }); priceHistory.currentPrice = currentPrice; saveSolanaData(priceHistory); } } function immediatePriceCheckAndAnnounce() { const solanaData = readSolanaData(); const lastKnownPrice = solanaData ? solanaData.price : null; 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 sixHourChange = { 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; } if (priceHistory.prices.length >= 360) { const sixHourAgoPrice = parseFloat(findPriceAgo(360).price); sixHourChange.percent = ((latestPrice - sixHourAgoPrice) / sixHourAgoPrice) * 100; sixHourChange.dollar = latestPrice - sixHourAgoPrice; } return { oneMinChange, fiveMinChange, oneHourChange, sixHourChange, oneDayChange }; } 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() { await fetchSolanaPriceAndUpdateHistory(); const { oneMinChange, fiveMinChange, oneHourChange, sixHourChange, oneDayChange } = calculateChanges(); console.log(`Current Price: ${priceHistory.currentPrice}`); const randomColor = Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0'); const embed = new EmbedBuilder() .setColor(`#${randomColor}`) .setThumbnail('https://solana.com/src/img/branding/solanaLogoMark.png') .setTitle('Solana (SOL) Price Update') .setURL('https://coinmarketcap.com/currencies/solana/') .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: '6 Hour Change', value: `${sixHourChange.percent.toFixed(2)}% (${sixHourChange.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 { const message = await solanaPriceChannel.messages.fetch(lastPriceMessageId); await message.edit({ embeds: [embed] }); } catch (error) { console.error('Error updating price message, sending a new one:', error); sendNewPriceMessage(solanaPriceChannel, embed); } } else { console.log('No lastPriceMessageId found, sending a new message.'); sendNewPriceMessage(solanaPriceChannel, embed); } if (lastAnnouncedPrice !== null && (parseFloat(priceHistory.currentPrice) - lastAnnouncedPrice >= 2.5)) { const announcementsChannel = await client.channels.fetch(announcementsChannelId); await announcementsChannel.send(`@everyone Solana price has increased significantly! Current price: $${priceHistory.currentPrice}`); lastAnnouncedPrice = parseFloat(priceHistory.currentPrice); saveSolanaData({ ...priceHistory, lastAnnouncedPrice }); } setTimeout(checkPriceContinuously, 60000); } function saveSolanaData(data) { const dir = path.dirname(solanaDataFile); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } const dataToSave = { ...data, lastAnnouncedPrice: lastAnnouncedPrice, }; fs.writeFileSync(solanaDataFile, JSON.stringify(dataToSave, null, 2), 'utf8'); } function readSolanaData() { try { if (fs.existsSync(solanaDataFile)) { const fileContent = fs.readFileSync(solanaDataFile, 'utf8'); const data = JSON.parse(fileContent); lastAnnouncedPrice = data.lastAnnouncedPrice || null; return data; } } catch (error) { console.error('Error reading Solana data:', error); } return null; } client.login(process.env.DISCORD_BOT_TOKEN);