mirror of
https://github.com/aljazceru/plugins.git
synced 2025-12-25 17:04:20 +01:00
137 lines
5.4 KiB
Python
Executable File
137 lines
5.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
from pyln.client import Plugin
|
|
from collections import namedtuple
|
|
from pyln.client import Millisatoshi
|
|
from cachetools import cached, TTLCache
|
|
import requests
|
|
import statistics
|
|
import time
|
|
|
|
plugin = Plugin()
|
|
|
|
Source = namedtuple('Source', ['name', 'urlformat', 'replymembers'])
|
|
|
|
sources = [
|
|
# e.g. {"GBP": {"volume_btc": "24.36647424", "rates": {"last": "13667.63"}, "avg_1h": "13786.86", "avg_6h": "13723.65", "avg_12h": "13680.23", "avg_24h": "13739.56"}, "USD": {"volume_btc": "27.97517017", "rates": {"last": "18204.21"}, "avg_1h": "19349.46", "avg_6h": "18621.72", "avg_12h": "18642.28", "avg_24h": "18698.94"}
|
|
# ...
|
|
# "GTQ": {"volume_btc": "0.03756101", "rates": {"last": "148505.46"}, "avg_1h": "148505.46", "avg_6h": "162463.69", "avg_12h": "162003.10", "avg_24h": "162003.10"}, "DKK": {"volume_btc": "0.00339923", "rates": {"last": "139737.53"}, "avg_12h": "139737.53", "avg_24h": "139737.53"}, "HTG": {"volume_btc": "0.00024758", "rates": {"last": "2019549.24"}, "avg_6h": "2019549.24", "avg_12h": "2019549.24", "avg_24h": "2019549.24"}, "NAD": {"volume_btc": "0.00722222", "rates": {"last": "360000.11"}, "avg_12h": "360000.11", "avg_24h": "360000.11"}}
|
|
Source('localbitcoins',
|
|
'https://localbitcoins.com/bitcoinaverage/ticker-all-currencies/',
|
|
['{currency}', "avg_6h"]),
|
|
# e.g. {"high": "18502.56", "last": "17970.41", "timestamp": "1607650787", "bid": "17961.87", "vwap": "18223.42", "volume": "7055.63066541", "low": "17815.92", "ask": "17970.41", "open": "18250.30"}
|
|
Source('bitstamp',
|
|
'https://www.bitstamp.net/api/v2/ticker/btc{currency_lc}/',
|
|
['last']),
|
|
# e.g. {"bitcoin":{"usd":17885.84}}
|
|
Source('coingecko',
|
|
'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies={currency_lc}',
|
|
['bitcoin', '{currency_lc}']),
|
|
]
|
|
|
|
|
|
def get_currencyrate(plugin, currency, req_template, response_members):
|
|
# NOTE: Bitstamp has a DNS/Proxy issues that can return 404
|
|
# Workaround: retry up to 5 times with a delay
|
|
currency_lc = currency.lower()
|
|
url = req_template.format(currency_lc=currency_lc, currency=currency)
|
|
for _ in range(5):
|
|
r = requests.get(url, proxies=plugin.proxies)
|
|
if r.status_code != 200:
|
|
time.sleep(1)
|
|
continue
|
|
break
|
|
|
|
if r.status_code != 200:
|
|
plugin.log(level='info', message='{}: bad response {}'.format(url, r.status_code))
|
|
return None
|
|
|
|
json = r.json()
|
|
for m in response_members:
|
|
expanded = m.format(currency_lc=currency_lc, currency=currency)
|
|
if expanded not in json:
|
|
plugin.log(level='debug', message='{}: {} not in {}'.format(url, expanded, json))
|
|
return None
|
|
json = json[expanded]
|
|
|
|
try:
|
|
return Millisatoshi(int(10**11 / float(json)))
|
|
except Exception:
|
|
plugin.log(level='info', message='{}: could not convert {} to msat'.format(url, json))
|
|
return None
|
|
|
|
|
|
def set_proxies(plugin):
|
|
config = plugin.rpc.listconfigs()
|
|
if 'always-use-proxy' in config and config['always-use-proxy']:
|
|
paddr = config['proxy']
|
|
# Default port in 9050
|
|
if ':' not in paddr:
|
|
paddr += ':9050'
|
|
plugin.proxies = {'https': 'socks5h://' + paddr,
|
|
'http': 'socks5h://' + paddr}
|
|
else:
|
|
plugin.proxies = None
|
|
|
|
|
|
# Don't grab these more than once per hour.
|
|
@cached(cache=TTLCache(maxsize=1024, ttl=3600))
|
|
def get_rates(plugin, currency):
|
|
rates = {}
|
|
for s in sources:
|
|
r = get_currencyrate(plugin, currency, s.urlformat, s.replymembers)
|
|
if r is not None:
|
|
rates[s.name] = r
|
|
|
|
return rates
|
|
|
|
|
|
@plugin.method("currencyrate")
|
|
def currencyrate(plugin, currency):
|
|
"""Gets currency from given APIs."""
|
|
|
|
return get_rates(plugin, currency.upper())
|
|
|
|
|
|
@plugin.method("currencyconvert")
|
|
def currencyconvert(plugin, amount, currency):
|
|
"""Converts currency using given APIs."""
|
|
rates = get_rates(plugin, currency.upper())
|
|
if len(rates) == 0:
|
|
raise Exception("No values available for currency {}".format(currency.upper()))
|
|
val = statistics.median([m.millisatoshis for m in rates.values()]) * float(amount)
|
|
return {"msat": Millisatoshi(round(val))}
|
|
|
|
|
|
@plugin.init()
|
|
def init(options, configuration, plugin):
|
|
set_proxies(plugin)
|
|
|
|
if options['add-source'] != '':
|
|
sourceopts = options['add-source']
|
|
# Prior to 0.9.3, 'multi' was unsupported.
|
|
if type(sourceopts) is not list:
|
|
sourceopts = [sourceopts]
|
|
for s in sourceopts:
|
|
parts = s.split(',')
|
|
sources.append(Source(parts[0], parts[1], parts[2:]))
|
|
|
|
if options['disable-source'] != '':
|
|
disableopts = options['disable-source']
|
|
# Prior to 0.9.3, 'multi' was unsupported.
|
|
if type(disableopts) is not list:
|
|
disableopts = [disableopts]
|
|
for s in sources[:]:
|
|
if s.name in disableopts:
|
|
sources.remove(s)
|
|
|
|
|
|
# As a bad example: binance,https://api.binance.com/api/v3/ticker/price?symbol=BTC{currency}T,price
|
|
plugin.add_option(name='add-source', default='', description='Add source name,urlformat,resultmembers...')
|
|
plugin.add_option(name='disable-source', default='', description='Disable source by name')
|
|
|
|
# This has an effect only for recent pyln versions (0.9.3+).
|
|
plugin.options['add-source']['multi'] = True
|
|
plugin.options['disable-source']['multi'] = True
|
|
|
|
plugin.run()
|