mirror of
https://github.com/aljazceru/plugins.git
synced 2025-12-21 07:04:19 +01:00
Merge pull request #1 from rustyrussell/summary
Summary: simple plugin which got out of hand.
This commit is contained in:
57
summary/README.md
Normal file
57
summary/README.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Summary plugin
|
||||
|
||||
This plugin is a little hack to show a summary of your node, including
|
||||
fiat amounts. If you have pylightning 0.0.7.1 or above, you get nice linegraphs,
|
||||
otherwise normal ASCII.
|
||||
|
||||
## Options:
|
||||
|
||||
* --summary-currency: Currency ticker to look up on bitaverage (default: `USD`)
|
||||
* --summary-currency-prefix: Prefix when printing currency (default: `USD $`)
|
||||
|
||||
## Example Usage
|
||||
|
||||
Unfortunately the python plugin framework doesn't pretty-print, nor does
|
||||
lightning-cli, so best viewed with -H:
|
||||
|
||||
```
|
||||
$ lightning-cli -H summary
|
||||
network=TESTNET
|
||||
my_address=031a3478d481b92e3c28810228252898c5f0d82fc4d07f5210c4f34d4aba56b769@165.227.30.200
|
||||
num_utxos=5
|
||||
utxo_amount=1.20119332000btc (USD $4473.84)
|
||||
num_channels=29
|
||||
num_connected=2
|
||||
num_gossipers=1
|
||||
avail_out=0.27095103btc (USD $1009.16)
|
||||
avail_in=2.05851379btc (USD $7666.93)
|
||||
channels_key=P=private O=offline
|
||||
channels= ├────────────╢ (O):02ac05912f89e43b88de3472e8c3003b
|
||||
├───────────╢ (O):02dd4cef0192611bc34cd1c3a0a7eb0f
|
||||
╟────────────┤ (PO):02a13878947a133d7c96e70303a9bf27
|
||||
║ (O):033e2db012833d997e3c
|
||||
╟┤ (O):Kenny_Loggins
|
||||
╟──────────────────────┤(O):DeutscheTestnetBank
|
||||
╟─────────────────────┤ (O):BlueLagoon1
|
||||
╟──────────────────────┤(O):0270dd38e8af9a64b4a483ab12b6aeb1
|
||||
╟┤ (O):btctest.lnetwork.tokyo
|
||||
╟─┤ (O):microbet.fun
|
||||
╟──────────────────────┤(PO):02fcab6e34a2ad21be2a752ab96d13f5
|
||||
╟──────────────────────┤(O):htlc.me
|
||||
╟───┤ (O):02229ea9a7a4f9bf8bf25ce225079aed
|
||||
╟─────────────────────┤ (O):025d5b572a94235cfcbdc429181b2b88
|
||||
╟────────────┤ (PO):03c56de3a84336b4a939777ace9ecbef
|
||||
╟────────┤ (O):LiteStrikeBTClnd
|
||||
╟────────────────┤ (PO):037c9cf1cde4414c59407d547b7eac08
|
||||
║ (O):03490a74e4def9125a84aee2d84e8cfe
|
||||
├─────────┼─────────┤ (O):aranguren.org
|
||||
║ (PO):03cc6603e1f6df535dd8b423284f2c09
|
||||
║ (O):cyclopes
|
||||
╟─────────────────────┤ (PO):02b73a2160863e925e9fa978b0ddc56b
|
||||
╟───┤ (O):lnd-testnet.ignios.net
|
||||
╟─┤ (PO):0327a104108173d4a4f34ab2cbc3084c
|
||||
╟─┤ :dwarf
|
||||
║ (PO):028133777757ce281658804dd82f5758
|
||||
╟────────────┤ (PO):02db62ffff5c35be74e7f856bba136db
|
||||
╟┤ (PO):03015ac044f5fa9768ededf6fed9c0ff
|
||||
╟──────────────────────┤:0270685ca81a8e4d4d01
|
||||
3
summary/requirements.txt
Normal file
3
summary/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
pylightning>=0.0.6
|
||||
requests>=2.0.0
|
||||
packaging>=14.1
|
||||
202
summary/summary.py
Executable file
202
summary/summary.py
Executable file
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/env python3
|
||||
from lightning import Plugin, Millisatoshi
|
||||
from packaging import version
|
||||
from collections import namedtuple
|
||||
import lightning
|
||||
import json
|
||||
import requests
|
||||
import threading
|
||||
import time
|
||||
|
||||
plugin = Plugin(autopatch=True)
|
||||
|
||||
have_utf8 = False
|
||||
|
||||
# __version__ was introduced in 0.0.7.1, with utf8 passthrough support.
|
||||
try:
|
||||
if version.parse(lightning.__version__) >= version.parse("0.0.7.1"):
|
||||
have_utf8 = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
Charset = namedtuple('Charset', ['double_left', 'left', 'bar', 'mid', 'right', 'double_right', 'empty'])
|
||||
if have_utf8:
|
||||
draw = Charset('╟', '├', '─', '┼', '┤', '╢', '║')
|
||||
else:
|
||||
draw = Charset('#', '[', '-', '/', ']', '#', '|')
|
||||
|
||||
|
||||
class PriceThread(threading.Thread):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.daemon = True
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
r = requests.get('https://apiv2.bitcoinaverage.com/convert/global'
|
||||
'?from=BTC&to={}&amount=1'.format(plugin.currency))
|
||||
plugin.fiat_per_btc = json.loads(r.content)['price']
|
||||
except Exception:
|
||||
pass
|
||||
# Six hours is more than often enough for polling
|
||||
time.sleep(6*3600)
|
||||
|
||||
|
||||
def to_fiatstr(msat: Millisatoshi):
|
||||
return "{}{:.2f}".format(plugin.currency_prefix,
|
||||
int(msat) / 10**11 * plugin.fiat_per_btc)
|
||||
|
||||
|
||||
@plugin.method("summary")
|
||||
def summary(plugin):
|
||||
"""Gets summary information about this node."""
|
||||
|
||||
reply = {}
|
||||
info = plugin.rpc.getinfo()
|
||||
funds = plugin.rpc.listfunds()
|
||||
peers = plugin.rpc.listpeers()
|
||||
|
||||
# Make it stand out if we're not on mainnet.
|
||||
if info['network'] != 'bitcoin':
|
||||
reply['network'] = info['network'].upper()
|
||||
|
||||
if not plugin.my_address:
|
||||
reply['warning_no_address'] = "NO PUBLIC ADDRESSES"
|
||||
else:
|
||||
reply['my_address'] = plugin.my_address
|
||||
|
||||
|
||||
utxos = [int(f['amount_msat']) for f in funds['outputs']
|
||||
if f['status'] == 'confirmed']
|
||||
reply['num_utxos'] = len(utxos)
|
||||
utxo_amount = Millisatoshi(sum(utxos))
|
||||
reply['utxo_amount'] = utxo_amount.to_btc_str()
|
||||
|
||||
avail_out = Millisatoshi(0)
|
||||
avail_in = Millisatoshi(0)
|
||||
chans = []
|
||||
reply['num_channels'] = 0
|
||||
reply['num_connected'] = 0
|
||||
reply['num_gossipers'] = 0
|
||||
for p in peers['peers']:
|
||||
active_channel = False
|
||||
for c in p['channels']:
|
||||
if c['state'] != 'CHANNELD_NORMAL':
|
||||
continue
|
||||
active_channel = True
|
||||
if p['connected']:
|
||||
reply['num_connected'] += 1
|
||||
if c['our_reserve_msat'] < c['to_us_msat']:
|
||||
to_us = c['to_us_msat'] - c['our_reserve_msat']
|
||||
else:
|
||||
to_us = Millisatoshi(0)
|
||||
avail_out += to_us
|
||||
|
||||
# We have to derive amount to them
|
||||
to_them = c['total_msat'] - c['to_us_msat']
|
||||
if c['their_reserve_msat'] < to_them:
|
||||
to_them = to_them - c['their_reserve_msat']
|
||||
else:
|
||||
to_them = Millisatoshi(0)
|
||||
avail_in += to_them
|
||||
reply['num_channels'] += 1
|
||||
chans.append((c['total_msat'], to_us, to_them, p['id'], c['private'], p['connected']))
|
||||
|
||||
if not active_channel and p['connected']:
|
||||
reply['num_gossipers'] += 1
|
||||
|
||||
reply['avail_out'] = avail_out.to_btc_str()
|
||||
reply['avail_in'] = avail_in.to_btc_str()
|
||||
|
||||
if plugin.fiat_per_btc:
|
||||
reply['utxo_amount'] += ' ({})'.format(to_fiatstr(utxo_amount))
|
||||
reply['avail_out'] += ' ({})'.format(to_fiatstr(avail_out))
|
||||
reply['avail_in'] += ' ({})'.format(to_fiatstr(avail_in))
|
||||
|
||||
if chans != []:
|
||||
reply['channels_key'] = 'P=private O=offline'
|
||||
reply['channels'] = []
|
||||
biggest = max(max(int(c[1]), int(c[2])) for c in chans)
|
||||
for c in chans:
|
||||
# Create simple line graph, 47 chars wide.
|
||||
our_len = int((int(c[1]) / biggest * 23))
|
||||
their_len = int((int(c[2]) / biggest * 23))
|
||||
divided = False
|
||||
|
||||
# We put midpoint in the middle.
|
||||
mid = draw.mid
|
||||
if our_len == 0:
|
||||
left = "{:>23}".format('')
|
||||
mid = draw.double_left
|
||||
else:
|
||||
left = "{:>23}".format(draw.left + draw.bar * (our_len - 1))
|
||||
|
||||
if their_len == 0:
|
||||
right = "{:23}".format('')
|
||||
# Both 0 is a special case.
|
||||
if our_len == 0:
|
||||
mid = draw.empty
|
||||
else:
|
||||
mid = draw.double_right
|
||||
else:
|
||||
right = "{:23}".format(draw.bar * (their_len - 1) + draw.right)
|
||||
|
||||
s = left + mid + right
|
||||
|
||||
extra = ''
|
||||
if c[4]:
|
||||
extra += 'P'
|
||||
if not c[5]:
|
||||
extra += 'O'
|
||||
if extra != '':
|
||||
s += '({})'.format(extra)
|
||||
|
||||
node = plugin.rpc.listnodes(c[3])['nodes']
|
||||
if len(node) != 0:
|
||||
s += ':' + node[0]['alias']
|
||||
else:
|
||||
s += ':' + c[3][0:32]
|
||||
reply['channels'].append(s)
|
||||
|
||||
return reply
|
||||
|
||||
|
||||
@plugin.init()
|
||||
def init(options, configuration, plugin):
|
||||
plugin.currency = options['summary-currency']
|
||||
plugin.currency_prefix = options['summary-currency-prefix']
|
||||
info = plugin.rpc.getinfo()
|
||||
|
||||
# Try to grab conversion price
|
||||
PriceThread()
|
||||
|
||||
# Prefer IPv4, otherwise take any to give out address.
|
||||
best_address = None
|
||||
for a in info['address']:
|
||||
if best_address is None:
|
||||
best_address = a
|
||||
elif a['type'] == 'ipv4' and best_address['type'] != 'ipv4':
|
||||
best_address = a
|
||||
|
||||
if best_address:
|
||||
plugin.my_address = info['id'] + '@' + best_address['address']
|
||||
if best_address['port'] != 9735:
|
||||
plugin.my_address += ':' + str(best_address['port'])
|
||||
else:
|
||||
plugin.my_address = None
|
||||
|
||||
plugin.log("Plugin summary.py initialized")
|
||||
|
||||
|
||||
plugin.add_option(
|
||||
'summary-currency',
|
||||
'USD',
|
||||
'What currency should I look up on btcaverage?'
|
||||
)
|
||||
plugin.add_option(
|
||||
'summary-currency-prefix',
|
||||
'USD $',
|
||||
'What prefix to use for currency'
|
||||
)
|
||||
plugin.run()
|
||||
Reference in New Issue
Block a user