131 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#!/usr/bin/env python3
 | 
						|
 | 
						|
# A shell script that converts one currency to another.
 | 
						|
#
 | 
						|
# Usage: shekel AMOUNT FROM TO
 | 
						|
#
 | 
						|
# FROM and TO are either currency codes or a fuzzy substring (requires iterfzf
 | 
						|
# and fzf).
 | 
						|
#
 | 
						|
#   Examples:
 | 
						|
#     shekel 200 USD CAD
 | 
						|
#   Don't remember the currency code? Use fuzzy search:
 | 
						|
#     shekel 200 USD british
 | 
						|
#   This will spawn an fzf session (requires the iterfzf package and fzf)
 | 
						|
#
 | 
						|
# Requires: iterfzf (optional)
 | 
						|
 | 
						|
import sys
 | 
						|
import os
 | 
						|
import json
 | 
						|
from urllib import request
 | 
						|
from datetime import datetime
 | 
						|
from base64 import b64encode
 | 
						|
 | 
						|
try:
 | 
						|
    from iterfzf import iterfzf
 | 
						|
except:
 | 
						|
    pass
 | 
						|
 | 
						|
 | 
						|
API_KEY = "92a7ccb3b6038a7151fc15e99f2de83d" # please use your own!
 | 
						|
API_URL = "http://apilayer.net/api/live?access_key={}¤cies={},{}"
 | 
						|
DB_URL = "https://raw.githubusercontent.com/hlissner/lb6-actions/master/Convert%20Currency.lbaction/Contents/Resources/currencies.json"
 | 
						|
 | 
						|
DEFAULT_CODE = "CAD"
 | 
						|
CACHE_FILE = os.path.expanduser("~/.cache/shekel-hist")
 | 
						|
CACHE_TTL = 3600
 | 
						|
CACHE = {}
 | 
						|
 | 
						|
def _request(url, *args):
 | 
						|
    try:
 | 
						|
        real_url = url.format(*map(request.quote, map(str, args)))
 | 
						|
        resp = request.urlopen(real_url).read().decode('utf-8')
 | 
						|
        data = json.loads(resp)
 | 
						|
        if data['success']:
 | 
						|
            return data["quotes"]
 | 
						|
        raise Exception(data['error']['info'])
 | 
						|
    except IOError:
 | 
						|
        print("IOError")
 | 
						|
 | 
						|
def _cache(key, value):
 | 
						|
    CACHE[key] = {
 | 
						|
        'time': datetime.now().timestamp(),
 | 
						|
        'value': value
 | 
						|
    }
 | 
						|
 | 
						|
def cache(from_code, to_code, fn, ttl=CACHE_TTL):
 | 
						|
    key = "-".join([from_code, to_code])
 | 
						|
    if key not in CACHE or CACHE[key]['time'] > CACHE[key]['time'] + ttl:
 | 
						|
        value = fn()
 | 
						|
        if value is not None:
 | 
						|
            _cache(key, value)
 | 
						|
            _cache("-".join([to_code, from_code]), value)
 | 
						|
    return CACHE[key]['value']
 | 
						|
 | 
						|
def get_rate(from_code, to_code=DEFAULT_CODE):
 | 
						|
    if from_code == to_code:
 | 
						|
        raise ValueError("Trying to convert currency to itself ({})".format(from_code))
 | 
						|
    elif from_code is None or to_code is None:
 | 
						|
        return None
 | 
						|
    rates = cache(
 | 
						|
        from_code,
 | 
						|
        to_code,
 | 
						|
        lambda: _request(API_URL, API_KEY, from_code, to_code)
 | 
						|
    )
 | 
						|
    src = rates["USD" + from_code]
 | 
						|
    dest = rates["USD" + to_code]
 | 
						|
    return (1 / src) * dest
 | 
						|
 | 
						|
def parse(codes, db):
 | 
						|
    for i, code in enumerate(codes):
 | 
						|
        if len(code) == 3 and code.isupper():
 | 
						|
            continue
 | 
						|
        resp = iterfzf(currencies(), query=code)
 | 
						|
        if resp is None:
 | 
						|
            codes[i] = None
 | 
						|
        else:
 | 
						|
            codes[i] = resp.split("  ")[0]
 | 
						|
 | 
						|
    return codes
 | 
						|
 | 
						|
def currencies():
 | 
						|
    for curr in CACHE['_DB']:
 | 
						|
        yield "{}  {}".format(curr['id'], curr['currencyName'])
 | 
						|
 | 
						|
def usage():
 | 
						|
    print("Usage: {} AMOUNT FROM [TO]".format(sys.argv[0]))
 | 
						|
 | 
						|
#
 | 
						|
if __name__ == '__main__':
 | 
						|
    refresh = False
 | 
						|
    if "-f" in sys.argv:
 | 
						|
        refresh = True
 | 
						|
        sys.argv.remove('-f')
 | 
						|
 | 
						|
    if len(sys.argv) < 3:
 | 
						|
        usage()
 | 
						|
        sys.exit(1)
 | 
						|
 | 
						|
    if refresh:
 | 
						|
        CACHE = {}
 | 
						|
    else:
 | 
						|
        if os.path.isfile(CACHE_FILE):
 | 
						|
            with open(CACHE_FILE, 'r') as f:
 | 
						|
                CACHE = json.load(f)
 | 
						|
    if "_DB" not in CACHE:
 | 
						|
        CACHE['_DB'] = json.loads(request.urlopen(DB_URL).read().decode('utf-8'))
 | 
						|
 | 
						|
    amount, codes = float(sys.argv[1]), parse(sys.argv[2:4], CACHE['_DB'])
 | 
						|
    rate = get_rate(*codes)
 | 
						|
    if rate is None:
 | 
						|
        print("Aborted")
 | 
						|
    else:
 | 
						|
        converted_amount = amount * rate
 | 
						|
        print("{:.6f} {}".format(converted_amount, codes[1]))
 | 
						|
 | 
						|
    with open(CACHE_FILE, 'w') as f:
 | 
						|
        json.dump(CACHE, f)
 | 
						|
 | 
						|
    sys.exit(0)
 |