mirror of
https://github.com/fawazahmed0/exchange-api.git
synced 2025-06-29 18:21:14 +00:00
223 lines
8.3 KiB
JavaScript
223 lines
8.3 KiB
JavaScript
const fs = require('fs-extra')
|
|
const path = require('path')
|
|
const { firefox, devices } = require('playwright')
|
|
const semver = require('semver')
|
|
|
|
|
|
// spaces to be used for prettify/json.stringify
|
|
const indent = '\t'
|
|
|
|
// By market capitalization
|
|
// Ref: https://coinmarketcap.com/all/views/all/
|
|
let topCryptoCurrency = ["BTC","ETH","ADA","BNB","USDT","XRP","SOL","DOT","DOGE","USDC","UNI","LUNA","LINK","AVAX","LTC","BUSD","AAVE","FRAX","HBAR",
|
|
"BCH","ALGO","WBTC","ICP","MATIC","FIL","TRX","FTT","XLM","VET","ATOM","ETC","THETA","DAI","XMR","MANA","ZEC","TUSD","EOS","AXS",
|
|
"ONE","EGLD","CHZ","GRT","1INCH","INJ","ENJ","KSM","CRO","SHIB","LEO","NEAR","BTCB","FLOW","XTZ","KCS", "SAND","KLAY","MKR",
|
|
"HT","DFI","TTT","WAVES","HNT","BSV","USDP","MIOTA","FTM","XEC","RUNE","QNT","NEO","CAKE","STX","LRC","OKB","NEXO","ZIL","DASH",
|
|
"PAXG","CELO","BAT","CVX","CRV","KAVA","GALA","DCR","GNO","AMP","XDC","WEMIX","XEM","MINA","HOT","AR","GT","FEI","COMP","QTUM",
|
|
"KNC","BTG","KDA","mBTC","uBTC","mETH","XCH","BSW","BAKE","KAS", "TON", "RVN","LDO","MNT","APT","ARB","OP","USDD","IMX","APE","SNX",
|
|
"RNDR","RPL","XAUt","FXS","PEPE","CFX","CSPR","BTT","SUI","LUNC","GUSD","TWT","GMX","AKT","NFT","FLR","DYDX","WOO","MBX","AGIX",
|
|
"ORDI","1000SATS"]
|
|
|
|
let currencyCodesToRemove = ["CLF"]
|
|
|
|
let currLink, currLink2, currLink3, currLink3Key, cryptoLink
|
|
if (process.env.CI) {
|
|
currLink = process.env.currlink
|
|
currLink2 = process.env.currlink2
|
|
currLink3 = process.env.currlink3
|
|
currLink3Key = process.env.currlink3key
|
|
cryptoLink = process.env.cryptolink
|
|
} else {
|
|
[currLink, currLink2, currLink3, currLink3Key, cryptoLink] = fs.readFileSync(path.join(__dirname, 'links.ini')).toString().split(/\r?\n/).map(e => e.trim())
|
|
}
|
|
// curr means currency
|
|
// stores consolidated currencies in currcode:currname format i.e USD:US Dollar
|
|
let allcurr = fs.readFileSync(path.join(__dirname, 'allcurrencies.min.json')).toString()
|
|
allcurr = JSON.parse(allcurr)
|
|
|
|
// Takes allcurr and have all the keys in uppercase
|
|
const allcurrKeyUpper = {}
|
|
for (const [key, value] of Object.entries(allcurr)) { allcurrKeyUpper[key.toUpperCase()] = value }
|
|
|
|
// Takes allcurr and store in lowercase currname:currcode format i.e. us dollar:usd
|
|
const allcurrLower = {}
|
|
for (const [key, value] of Object.entries(allcurr)) { allcurrLower[value.toLowerCase()] = key.toLowerCase() }
|
|
|
|
const dateToday = new Date().toISOString().substring(0, 10)
|
|
const dateTodaySemVer = semver.clean(dateToday.replaceAll('-','.'), true)
|
|
|
|
const pathToSkeletonPackage = path.join(__dirname, 'skeleton-package.json')
|
|
|
|
const apiVersion = 1
|
|
|
|
const rootDir = path.join(__dirname, 'package', `v${apiVersion}`)
|
|
|
|
begin()
|
|
// Begins the program
|
|
async function begin() {
|
|
// launch the browser
|
|
// await launchBrowser()
|
|
|
|
|
|
|
|
const currJSON = await getCurrencies()
|
|
// Get & Save All the available currencies in api
|
|
const availCurrListObj = await getAvailCurrencyJSON(currJSON)
|
|
fs.outputFileSync(path.join(rootDir, 'currencies.min.json'), JSON.stringify(availCurrListObj))
|
|
fs.writeFileSync(path.join(rootDir, 'currencies.json'), JSON.stringify(availCurrListObj, null, indent))
|
|
|
|
// Generate API files
|
|
await generateFiles(currJSON)
|
|
|
|
// Set package version to dateToday
|
|
let barePackage = fs.readJsonSync(pathToSkeletonPackage)
|
|
barePackage['version'] = dateTodaySemVer
|
|
fs.writeJSONSync(path.join(rootDir, '..' ,"package.json"), barePackage)
|
|
fs.writeFileSync(path.join(rootDir, '..' ,"index.js"), "")
|
|
|
|
// Close the browser
|
|
// await browser.close()
|
|
}
|
|
|
|
|
|
// Returns all the available currencies in the API
|
|
async function getAvailCurrencyJSON(CurrObj) {
|
|
const availCurrListObj = {}
|
|
for (const key of Object.keys(CurrObj)) {
|
|
availCurrListObj[key] = allcurrKeyUpper[key.toUpperCase()] || ""
|
|
if(!allcurrKeyUpper[key.toUpperCase()])
|
|
console.log(key,"currency code doesn't exist in allcurrencies.min.json")
|
|
}
|
|
|
|
return availCurrListObj
|
|
}
|
|
|
|
async function getCurrencies() {
|
|
let currDataObj = await getCurrData(currLink)
|
|
currDataObj = toLowerCaseKeysBaseCurr(currDataObj)
|
|
|
|
let currDataObj2 = await getCurrData(currLink2)
|
|
// we need to convert base usd to eur
|
|
currDataObj2 = toLowerCaseKeysBaseCurr(currDataObj2, currDataObj['usd'])
|
|
|
|
let currDataObj3 = toLowerCaseKeysBaseCurr( (await getCurrData3()) )
|
|
// we need to convert base usd to eur
|
|
currDataObj3 = 'eur' in currDataObj3 ? toLowerCaseKeysBaseCurr(currDataObj3, 1/currDataObj3['eur']) : {}
|
|
|
|
let cryptoDataObj = await getCryptoData()
|
|
// we also need to convert base usd to eur
|
|
cryptoDataObj = toLowerCaseKeysBaseCurr(cryptoDataObj,currDataObj['usd'])
|
|
let CurrJSON = {...currDataObj2, ...currDataObj, ...cryptoDataObj, ...currDataObj3, eur: 1 }
|
|
currencyCodesToRemove.forEach(e=>delete CurrJSON?.[e.toLowerCase()])
|
|
// return sorted object
|
|
return sortObjByKeys(CurrJSON)
|
|
}
|
|
|
|
// Euro as base rates
|
|
async function getCurrData(link){
|
|
let response = await fetch(link)
|
|
let data = await response.json()
|
|
return data.rates
|
|
}
|
|
|
|
// USD as base rates
|
|
async function getCryptoData(){
|
|
let response = await fetch(cryptoLink)
|
|
let data = await response.json()
|
|
let cleanJSON = {}
|
|
for(let value of data.data.reverse())
|
|
cleanJSON[value.symbol] = value.quote.USD.price
|
|
|
|
return Object.fromEntries( // Dividing value by 1 to convert to 1 USD as base rate
|
|
Object.entries(cleanJSON).filter(([k, v]) => topCryptoCurrency.includes(k)).map(([k,v])=>[k,1/v]) )
|
|
}
|
|
|
|
async function getCurrData3(){
|
|
|
|
const browser = await firefox.launch({headless: true});
|
|
let arrRes = []
|
|
try{
|
|
|
|
const context = await browser.newContext({ ...devices['Desktop Firefox'] });
|
|
const page = await context.newPage();
|
|
|
|
const reg = new RegExp(currLink3Key, "gi")
|
|
page.on('response', async res => reg.test(res.url()) ? arrRes.push(await res.json().catch(console.error)) : '' );
|
|
for(let i=0;i<3;i++){
|
|
try{
|
|
await page.goto(currLink3)
|
|
break;
|
|
}catch(e){}
|
|
}
|
|
|
|
arrRes = arrRes.filter(e=>e?.rates?.USD == 1)
|
|
return arrRes.length > 0 ? arrRes[0].rates : {}
|
|
|
|
}catch(e){
|
|
console.error(e)
|
|
return {}
|
|
}finally{
|
|
await browser.close()
|
|
}
|
|
}
|
|
|
|
// convert object keys to lowercase and values to float
|
|
// tocurr parameter
|
|
// convert object having a base currency value to object of another base currency value
|
|
// For example: pass object with base currency as usd, pass tocurr as 1Eur to 1.17USD i.e 1.17 to convert object to base currency as EUR
|
|
function toLowerCaseKeysBaseCurr(obj, tocurr=1){
|
|
let newobj = {}
|
|
for(let [key,value] of Object.entries(obj)){
|
|
let tempValue = parseFloat(value)*parseFloat(tocurr)
|
|
if(Number.isFinite(tempValue))
|
|
newobj[key.toLowerCase()] = tempValue
|
|
}
|
|
return newobj
|
|
}
|
|
|
|
|
|
// Sorts an object by keys and returns the sorted object
|
|
function sortObjByKeys(obj) {
|
|
const sortedObj = {}
|
|
const sortedKeys = Object.keys(obj).sort()
|
|
for (const key of sortedKeys) { sortedObj[key] = obj[key] }
|
|
return sortedObj
|
|
}
|
|
|
|
// Generates the api files
|
|
async function generateFiles(googBingCurrJSON) {
|
|
const currenciesDir = path.join(rootDir, 'currencies')
|
|
fs.mkdirSync(currenciesDir, {
|
|
recursive: true
|
|
})
|
|
for (const [fromKey, fromValue] of Object.entries(googBingCurrJSON)) {
|
|
const tempObj = {}
|
|
tempObj['date'] = dateToday;
|
|
tempObj[fromKey] = {}
|
|
|
|
for (const [toKey, toValue] of Object.entries(googBingCurrJSON))
|
|
tempObj[fromKey][toKey] = currencyValue(fromValue, toValue)
|
|
|
|
fs.writeFileSync(path.join(currenciesDir, fromKey + '.min.json'), JSON.stringify(tempObj))
|
|
fs.writeFileSync(path.join(currenciesDir, fromKey + '.json'), JSON.stringify(tempObj, null, indent))
|
|
}
|
|
}
|
|
|
|
// return 1 fromCurr as base currency for toCurr
|
|
// fromCurr & toCurr is against 1 USD or 1 EUR or something common
|
|
// For example, if you pass 74 INR & 0.84 EUR and 1 INR = 0.011 Eur
|
|
// It returns 0.011 , with numbers upto 20 decimal places
|
|
function currencyValue(fromCurr, toCurr) {
|
|
return getSignificantNum(toCurr / fromCurr)
|
|
}
|
|
|
|
// If the value is very small, then return more significant digits
|
|
function getSignificantNum(num){
|
|
let minSignificantDigits = 8
|
|
if(num >= 0.1)
|
|
return parseFloat(num.toFixed(minSignificantDigits))
|
|
let strNum = num.toFixed(100)
|
|
let numOfZeros = strNum.match(/^0\.0+/i)[0].length-2
|
|
return parseFloat(num.toFixed(minSignificantDigits+numOfZeros))
|
|
}
|