Skip to content

Instantly share code, notes, and snippets.

@10c8
Last active May 1, 2022 07:05
Show Gist options
  • Save 10c8/8a4efb2d8512ae775e3ed911894fb642 to your computer and use it in GitHub Desktop.
Save 10c8/8a4efb2d8512ae775e3ed911894fb642 to your computer and use it in GitHub Desktop.
cash.deno.dev v0.1.6
import { opine } from 'https://deno.land/x/opine@2.1.4/mod.ts';
import { bold, italic, underline, gray, green, red } from 'https://deno.land/x/nanocolors@0.1.12/mod.ts';
import { plot } from 'https://deno.land/x/chart@1.0.1/mod.ts';
const VERSION = '0.1.6';
const DATA_URL = 'https://gist.github.com/10c8/8a4efb2d8512ae775e3ed911894fb642/raw/d18a9e26ce2eff5aca39090500009928265f7b66/data.json';
const APILAYER_URL = 'https://api.apilayer.com/exchangerates_data';
// API
const getNameForSymbol = async (symbol: string): Promise<string> => {
const res = await fetch(DATA_URL);
const data = await res.json();
if (!data.symbols.hasOwnProperty(symbol)) {
return symbol;
}
return data.symbols[symbol];
}
const convertFromTo = async (
codeFrom: string,
codeTo: string,
amount: number
): Promise<{
date: string;
rate: string;
amount: string;
}> => {
const endpoint = `/convert?from=${codeFrom}&to=${codeTo}&amount=${amount}`;
const headers = new Headers();
headers.append('apikey', Deno.env.get('APILAYER_KEY'));
try {
const res = await fetch(APILAYER_URL + endpoint, {
method: 'GET',
redirect: 'follow',
headers,
});
const data = await res.json();
return {
date: data.date.replaceAll('-', '/'),
rate: data.info.rate,
amount: data.result,
};
} catch (error) {
console.log(error);
return null;
}
};
const timeseriesGraph = async (
codeFrom: string,
codeTo: string,
startDate: Date,
endDate: Date
): Promise<string> => {
const start = startDate.toISOString().slice(0, 10);
const end = endDate.toISOString().slice(0, 10);
const endpoint = `/timeseries?start_date=${start}&end_date=${end}&base=${codeFrom}&symbols=${codeTo}`;
const headers = new Headers();
headers.append(
'apikey',
Deno.env.get('APILAYER_KEY') || 'ptzhB0zsVsbH66DyM7DdRMOZonUPjSLL'
);
try {
const res = await fetch(APILAYER_URL + endpoint, {
method: 'GET',
redirect: 'follow',
headers,
});
const data = await res.json();
try {
const points = Object.values(data.rates).map(x => x[codeTo.toUpperCase()]);
const graph = plot(points, {
colors: ['green'],
height: 8,
});
return graph.split('\n').map(x => x.substring(4)).join('\n') + '\n';
} catch (error) {
console.log(error);
return null;
}
} catch (error) {
console.log(error);
return null;
}
};
// Main code
const app = opine();
const errorMessage = (_req, res) => {
const message = [
red('Hmm, looks like something went wrong. Try again later.'),
'',
bold(italic(green('cash.deno.dev') + ' ' + gray(`by 10c8 | ver ${VERSION}`))),
];
res.append('Content-Type', 'text/plain');
res.send(message.join('\n') + '\n');
};
const usageMessage = async (_req, res) => {
const message = [
bold('Usage:'),
' curl -L cash.deno.dev/<FROM>/<TO>/<AMOUNT>[/<OPTIONS>]',
'',
bold('Options:'),
' p\tPlots the exchange rate for the last 30 days to a graph.',
'',
bold('Example:'),
' curl -L cash.deno.dev/USD/GBP/500/p',
'',
bold(italic(green('cash.deno.dev') + ' ' + gray(`by 10c8 | ver ${VERSION}`))),
];
res.append('Content-Type', 'text/plain');
res.send(message.join('\n') + '\n');
};
const mainRoute = async (req, res) => {
const { codeFrom, codeTo, amount, optionList } = req.params;
const options = {
plot: false,
};
if (optionList != null) {
const optionsArray = optionList.toUpperCase().split('');
options.plot = optionsArray.includes('P');
}
if (codeFrom == null || codeTo == null || amount == null) {
return usageMessage(req, res);
}
const result = await convertFromTo(codeFrom, codeTo, amount);
if (!result) {
return errorMessage(req, res);
}
// Get the currency name for each symbol
const nameFrom = await getNameForSymbol(codeFrom.toUpperCase());
const nameTo = await getNameForSymbol(codeTo.toUpperCase());
const message = [
`${bold(underline(nameFrom))} to ${bold(underline(nameTo))}`,
`${bold('Rate:')} ${result.rate}`,
'',
`${amount} ${codeFrom.toUpperCase()} ${italic(gray('->'))} ${result.amount} ${codeTo.toUpperCase()}`,
'',
bold(italic(green('cash.deno.dev') + ' ' + gray(`by 10c8 | ver ${VERSION} [data for ${result.date}]`))),
];
if (options.plot) {
// Generate price history graph
const graphDateTo = new Date(`${result.date.replaceAll('/', '-')}T00:00:00`);
const graphDateFrom = new Date();
graphDateFrom.setDate(graphDateTo.getDate() - 29);
const graph = await timeseriesGraph(codeFrom, codeTo, graphDateFrom, graphDateTo);
if (graph !== null) {
message.splice(3, 0, graph);
}
}
res.append('Content-Type', 'text/plain');
res.send(message.join('\n') + '\n');
};
app.get('/:codeFrom/:codeTo/:amount/:optionList', mainRoute);
app.get('/:codeFrom/:codeTo/:amount', mainRoute);
app.get('/*', usageMessage);
app.listen(
8000,
() => console.log('Server has started on http://localhost:8000 🚀'),
);
{
"symbols": {
"AED": "United Arab Emirates Dirham",
"AFN": "Afghan Afghani",
"ALL": "Albanian Lek",
"AMD": "Armenian Dram",
"ANG": "Netherlands Antillean Guilder",
"AOA": "Angolan Kwanza",
"ARS": "Argentine Peso",
"AUD": "Australian Dollar",
"AWG": "Aruban Florin",
"AZN": "Azerbaijani Manat",
"BAM": "Bosnia-Herzegovina Convertible Mark",
"BBD": "Barbadian Dollar",
"BDT": "Bangladeshi Taka",
"BGN": "Bulgarian Lev",
"BHD": "Bahraini Dinar",
"BIF": "Burundian Franc",
"BMD": "Bermudan Dollar",
"BND": "Brunei Dollar",
"BOB": "Bolivian Boliviano",
"BRL": "Brazilian Real",
"BSD": "Bahamian Dollar",
"BTC": "Bitcoin",
"BTN": "Bhutanese Ngultrum",
"BWP": "Botswanan Pula",
"BYN": "New Belarusian Ruble",
"BYR": "Belarusian Ruble",
"BZD": "Belize Dollar",
"CAD": "Canadian Dollar",
"CDF": "Congolese Franc",
"CHF": "Swiss Franc",
"CLF": "Chilean Unit of Account (UF)",
"CLP": "Chilean Peso",
"CNY": "Chinese Yuan",
"COP": "Colombian Peso",
"CRC": "Costa Rican Col\u00f3n",
"CUC": "Cuban Convertible Peso",
"CUP": "Cuban Peso",
"CVE": "Cape Verdean Escudo",
"CZK": "Czech Republic Koruna",
"DJF": "Djiboutian Franc",
"DKK": "Danish Krone",
"DOP": "Dominican Peso",
"DZD": "Algerian Dinar",
"EGP": "Egyptian Pound",
"ERN": "Eritrean Nakfa",
"ETB": "Ethiopian Birr",
"EUR": "Euro",
"FJD": "Fijian Dollar",
"FKP": "Falkland Islands Pound",
"GBP": "British Pound Sterling",
"GEL": "Georgian Lari",
"GGP": "Guernsey Pound",
"GHS": "Ghanaian Cedi",
"GIP": "Gibraltar Pound",
"GMD": "Gambian Dalasi",
"GNF": "Guinean Franc",
"GTQ": "Guatemalan Quetzal",
"GYD": "Guyanaese Dollar",
"HKD": "Hong Kong Dollar",
"HNL": "Honduran Lempira",
"HRK": "Croatian Kuna",
"HTG": "Haitian Gourde",
"HUF": "Hungarian Forint",
"IDR": "Indonesian Rupiah",
"ILS": "Israeli New Sheqel",
"IMP": "Manx pound",
"INR": "Indian Rupee",
"IQD": "Iraqi Dinar",
"IRR": "Iranian Rial",
"ISK": "Icelandic Kr\u00f3na",
"JEP": "Jersey Pound",
"JMD": "Jamaican Dollar",
"JOD": "Jordanian Dinar",
"JPY": "Japanese Yen",
"KES": "Kenyan Shilling",
"KGS": "Kyrgystani Som",
"KHR": "Cambodian Riel",
"KMF": "Comorian Franc",
"KPW": "North Korean Won",
"KRW": "South Korean Won",
"KWD": "Kuwaiti Dinar",
"KYD": "Cayman Islands Dollar",
"KZT": "Kazakhstani Tenge",
"LAK": "Laotian Kip",
"LBP": "Lebanese Pound",
"LKR": "Sri Lankan Rupee",
"LRD": "Liberian Dollar",
"LSL": "Lesotho Loti",
"LTL": "Lithuanian Litas",
"LVL": "Latvian Lats",
"LYD": "Libyan Dinar",
"MAD": "Moroccan Dirham",
"MDL": "Moldovan Leu",
"MGA": "Malagasy Ariary",
"MKD": "Macedonian Denar",
"MMK": "Myanma Kyat",
"MNT": "Mongolian Tugrik",
"MOP": "Macanese Pataca",
"MRO": "Mauritanian Ouguiya",
"MUR": "Mauritian Rupee",
"MVR": "Maldivian Rufiyaa",
"MWK": "Malawian Kwacha",
"MXN": "Mexican Peso",
"MYR": "Malaysian Ringgit",
"MZN": "Mozambican Metical",
"NAD": "Namibian Dollar",
"NGN": "Nigerian Naira",
"NIO": "Nicaraguan C\u00f3rdoba",
"NOK": "Norwegian Krone",
"NPR": "Nepalese Rupee",
"NZD": "New Zealand Dollar",
"OMR": "Omani Rial",
"PAB": "Panamanian Balboa",
"PEN": "Peruvian Nuevo Sol",
"PGK": "Papua New Guinean Kina",
"PHP": "Philippine Peso",
"PKR": "Pakistani Rupee",
"PLN": "Polish Zloty",
"PYG": "Paraguayan Guarani",
"QAR": "Qatari Rial",
"RON": "Romanian Leu",
"RSD": "Serbian Dinar",
"RUB": "Russian Ruble",
"RWF": "Rwandan Franc",
"SAR": "Saudi Riyal",
"SBD": "Solomon Islands Dollar",
"SCR": "Seychellois Rupee",
"SDG": "Sudanese Pound",
"SEK": "Swedish Krona",
"SGD": "Singapore Dollar",
"SHP": "Saint Helena Pound",
"SLL": "Sierra Leonean Leone",
"SOS": "Somali Shilling",
"SRD": "Surinamese Dollar",
"STD": "S\u00e3o Tom\u00e9 and Pr\u00edncipe Dobra",
"SVC": "Salvadoran Col\u00f3n",
"SYP": "Syrian Pound",
"SZL": "Swazi Lilangeni",
"THB": "Thai Baht",
"TJS": "Tajikistani Somoni",
"TMT": "Turkmenistani Manat",
"TND": "Tunisian Dinar",
"TOP": "Tongan Pa\u02bbanga",
"TRY": "Turkish Lira",
"TTD": "Trinidad and Tobago Dollar",
"TWD": "New Taiwan Dollar",
"TZS": "Tanzanian Shilling",
"UAH": "Ukrainian Hryvnia",
"UGX": "Ugandan Shilling",
"USD": "United States Dollar",
"UYU": "Uruguayan Peso",
"UZS": "Uzbekistan Som",
"VEF": "Venezuelan Bol\u00edvar Fuerte",
"VND": "Vietnamese Dong",
"VUV": "Vanuatu Vatu",
"WST": "Samoan Tala",
"XAF": "CFA Franc BEAC",
"XAG": "Silver (troy ounce)",
"XAU": "Gold (troy ounce)",
"XCD": "East Caribbean Dollar",
"XDR": "Special Drawing Rights",
"XOF": "CFA Franc BCEAO",
"XPF": "CFP Franc",
"YER": "Yemeni Rial",
"ZAR": "South African Rand",
"ZMK": "Zambian Kwacha (pre-2013)",
"ZMW": "Zambian Kwacha",
"ZWL": "Zimbabwean Dollar"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment