moshai-cli
Version:
A modern, fast Node.js CLI powered by arasadrahman
125 lines (104 loc) ⢠4.17 kB
JavaScript
const axios = require('axios');
const chalk = require('chalk').default;
const fs = require('fs');
const path = require('path');
const Table = require('cli-table3');
const CACHE_DIR = path.resolve(__dirname, 'cache');
const CACHE_FILE = path.join(CACHE_DIR, 'crypto_cache.json');
const CACHE_TTL = 24 * 60 * 60 * 1000;
const API_KEY = 'b35ef9c9aa3f714b9b952f997e7b29e492c648b53c8bbe3dc31b8ef06d18703c';
function formatPriceBDT(price) {
const num = Number(price);
return isNaN(num) ? 'N/A' : `ą§³${num.toFixed(2)}`;
}
function formatPriceUSD(price) {
const num = Number(price);
return isNaN(num) ? 'N/A' : `$${num.toFixed(2)}`;
}
function formatChangePercent(pct) {
const num = Number(pct);
if (isNaN(num)) return chalk.gray('N/A');
const fixed = `${Math.abs(num).toFixed(2)}%`;
return num > 0 ? chalk.green(`+${fixed}`) : num < 0 ? chalk.red(`-${fixed}`) : chalk.gray(fixed);
}
async function fetchUsdToBdtRate() {
try {
const res = await axios.get('https://open.er-api.com/v6/latest/USD');
return Number(res.data.rates.BDT) || 105;
} catch {
return 105;
}
}
async function fetchCryptoData() {
const res = await axios.get('https://rest.coincap.io/v3/assets?limit=5', {
headers: { Authorization: `Bearer ${API_KEY}` },
});
return res.data.data;
}
async function loadCache() {
if (!fs.existsSync(CACHE_FILE)) return null;
const stat = fs.statSync(CACHE_FILE);
if (Date.now() - stat.mtimeMs > CACHE_TTL) return null;
try {
const data = fs.readFileSync(CACHE_FILE, 'utf8');
return JSON.parse(data);
} catch {
return null;
}
}
async function saveCache(data) {
if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true });
fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
}
module.exports = class CryptoPriceCommand {
static signature = 'crypto';
static description = 'Show top 5 crypto prices in BDT and USD with 24h change';
async handle() {
if (!this.startSpinner) this.startSpinner = () => { };
if (!this.stopSpinner) this.stopSpinner = () => { };
this.startSpinner('Fetching crypto prices...');
try {
let data = await loadCache();
if (!data) {
const [coins, usdToBdt] = await Promise.all([
fetchCryptoData(),
fetchUsdToBdtRate(),
]);
data = coins.map(coin => {
const usd = Number(coin.priceUsd);
const bdt = usd * usdToBdt;
return {
Name: coin.name || '',
Symbol: (coin.symbol || '').toUpperCase(),
'Price (BDT)': formatPriceBDT(bdt),
'Price (USD)': formatPriceUSD(usd),
'Change 24h': formatChangePercent(coin.changePercent24Hr),
};
});
await saveCache(data);
}
this.stopSpinner();
console.log(chalk.green('\nš Top 5 Crypto Prices in BDT and USD (Cached 5 mins max)\n'));
// Create CLI table with custom headers and column widths
const table = new Table({
head: ['Name', 'Symbol', 'Price (BDT)', 'Price (USD)', 'Change 24h'],
colWidths: [15, 10, 18, 15, 12],
style: { head: ['cyan'] },
});
// Push rows into table (colors will render properly)
for (const row of data) {
table.push([
row.Name,
row.Symbol,
row['Price (BDT)'],
row['Price (USD)'],
row['Change 24h'],
]);
}
console.log(table.toString());
} catch (err) {
this.stopSpinner();
console.error(chalk.red('ā Failed to fetch crypto prices:'), err.message);
}
}
};