diff --git a/home.admin/config.scripts/blitz.subscriptions.ip2tor.py b/home.admin/config.scripts/blitz.subscriptions.ip2tor.py index 8da27a6ba..257fdc90b 100644 --- a/home.admin/config.scripts/blitz.subscriptions.ip2tor.py +++ b/home.admin/config.scripts/blitz.subscriptions.ip2tor.py @@ -1,28 +1,32 @@ #!/usr/bin/python3 -import sys -import locale -import requests +import codecs import json import math +import os +import sys import time -import datetime, time -import codecs, grpc, os +from datetime import datetime from pathlib import Path + +import grpc +import requests import toml from blitzpy import RaspiBlitzConfig - from lndlibs import rpc_pb2 as lnrpc from lndlibs import rpc_pb2_grpc as rpcstub -####### SCRIPT INFO ######### +##################### +# SCRIPT INFO +##################### # display config script info if len(sys.argv) <= 1 or sys.argv[1] == "-h" or sys.argv[1] == "help": print("# manage ip2tor subscriptions for raspiblitz") print("# blitz.subscriptions.ip2tor.py create-ssh-dialog servicename toraddress torport") print("# blitz.subscriptions.ip2tor.py shop-list shopurl") - print("# blitz.subscriptions.ip2tor.py shop-order shopurl servicename hostid toraddress:port duration msatsFirst msatsNext [description]") + print("# blitz.subscriptions.ip2tor.py shop-order shopurl servicename hostid toraddress:port duration " + "msatsFirst msatsNext [description]") print("# blitz.subscriptions.ip2tor.py subscriptions-list") print("# blitz.subscriptions.ip2tor.py subscriptions-renew secondsBeforeSuspend") print("# blitz.subscriptions.ip2tor.py subscription-cancel id") @@ -30,38 +34,44 @@ if len(sys.argv) <= 1 or sys.argv[1] == "-h" or sys.argv[1] == "help": print("# blitz.subscriptions.ip2tor.py ip-by-tor onionaddress") sys.exit(1) - - -####### BASIC SETTINGS ######### - +##################### +# BASIC SETTINGS +##################### session = requests.session() if Path("/mnt/hdd/raspiblitz.conf").is_file(): cfg = RaspiBlitzConfig() cfg.reload() - ENV="PROD" - #DEFAULT_SHOPURL="shopdeu2vdhazvmllyfagdcvlpflzdyt5gwftmn4hjj3zw2oyelksaid.onion" - DEFAULT_SHOPURL="ip2tor.fulmo.org" - LND_IP="127.0.0.1" - LND_ADMIN_MACAROON_PATH="/mnt/hdd/app-data/lnd/data/chain/{0}/{1}net/admin.macaroon".format(cfg.network.value,cfg.chain.value) - LND_TLS_PATH="/mnt/hdd/app-data/lnd/tls.cert" - # make sure to make requests thru TOR 127.0.0.1:9050 - session.proxies = {'http': 'socks5h://127.0.0.1:9050', 'https': 'socks5h://127.0.0.1:9050'} - SUBSCRIPTIONS_FILE="/mnt/hdd/app-data/subscriptions/subscriptions.toml" -else: - ENV="DEV" - print("# blitz.ip2tor.py (development env)") - DEFAULT_SHOPURL="ip2tor.fulmo.org" - LND_IP="192.168.178.95" - LND_ADMIN_MACAROON_PATH="/Users/rotzoll/Downloads/RaspiBlitzCredentials/admin.macaroon" - LND_TLS_PATH="/Users/rotzoll/Downloads/RaspiBlitzCredentials/tls.cert" - SUBSCRIPTIONS_FILE="/Users/rotzoll/Downloads/RaspiBlitzCredentials/subscriptions.toml" -if cfg.chain.value == "test": - is_testnet = True + if cfg.chain.value == "test": + is_testnet = True + else: + is_testnet = False + + ENV = "PROD" + # DEFAULT_SHOPURL="shopdeu2vdhazvmllyfagdcvlpflzdyt5gwftmn4hjj3zw2oyelksaid.onion" + DEFAULT_SHOPURL = "ip2tor.fulmo.org" + LND_IP = "127.0.0.1" + LND_ADMIN_MACAROON_PATH = "/mnt/hdd/app-data/lnd/data/chain/{0}/{1}net/admin.macaroon".format(cfg.network.value, + cfg.chain.value) + LND_TLS_PATH = "/mnt/hdd/app-data/lnd/tls.cert" + # make sure to make requests thru TOR 127.0.0.1:9050 + session.proxies = {'http': 'socks5h://127.0.0.1:9050', 'https': 'socks5h://127.0.0.1:9050'} + SUBSCRIPTIONS_FILE = "/mnt/hdd/app-data/subscriptions/subscriptions.toml" else: + ENV = "DEV" + print("# blitz.ip2tor.py (development env)") + DEFAULT_SHOPURL = "ip2tor.fulmo.org" + LND_IP = "192.168.178.95" + LND_ADMIN_MACAROON_PATH = "/Users/rotzoll/Downloads/RaspiBlitzCredentials/admin.macaroon" + LND_TLS_PATH = "/Users/rotzoll/Downloads/RaspiBlitzCredentials/tls.cert" + SUBSCRIPTIONS_FILE = "/Users/rotzoll/Downloads/RaspiBlitzCredentials/subscriptions.toml" + is_testnet = False -####### HELPER CLASSES ######### + +##################### +# HELPER CLASSES +##################### class BlitzError(Exception): def __init__(self, errorShort, errorLong="", errorException=None): @@ -69,11 +79,15 @@ class BlitzError(Exception): self.errorLong = str(errorLong) self.errorException = errorException -####### HELPER FUNCTIONS ######### + +##################### +# HELPER FUNCTIONS +##################### def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) + def handleException(e): if isinstance(e, BlitzError): eprint(e.errorLong) @@ -84,18 +98,21 @@ def handleException(e): print("error='{0}'".format(str(e))) sys.exit(1) + def parseDate(datestr): - return datetime.datetime.strptime(datestr,"%Y-%m-%dT%H:%M:%SZ") + return datetime.strptime(datestr, "%Y-%m-%dT%H:%M:%SZ") + def secondsLeft(dateObj): - return round((dateObj - datetime.datetime.utcnow()).total_seconds()) + return round((dateObj - datetime.utcnow()).total_seconds()) + # takes a shopurl from user input and turns it into the format needed # also makes sure that .onion addresses run just with http not https def normalizeShopUrl(shopurlUserInput): - # basic checks and formats - if len(shopurlUserInput) < 3: return "" + if len(shopurlUserInput) < 3: + return "" shopurlUserInput = shopurlUserInput.lower() shopurlUserInput = shopurlUserInput.replace(" ", "") shopurlUserInput = shopurlUserInput.replace("\n", "") @@ -103,14 +120,14 @@ def normalizeShopUrl(shopurlUserInput): # remove protocol from the beginning (if needed) if shopurlUserInput.find("://") > 0: - shopurlUserInput = shopurlUserInput[shopurlUserInput.find("://")+3:] + shopurlUserInput = shopurlUserInput[shopurlUserInput.find("://") + 3:] # remove all path after domain if shopurlUserInput.find("/") > 0: shopurlUserInput = shopurlUserInput[:shopurlUserInput.find("/")] # add correct protocol again - if ( not shopurlUserInput.startswith("http://") and not shopurlUserInput.startswith("https://") ): + if not shopurlUserInput.startswith("http://") and not shopurlUserInput.startswith("https://"): if shopurlUserInput.endswith(".onion"): shopurlUserInput = "http://{0}".format(shopurlUserInput) else: @@ -118,78 +135,86 @@ def normalizeShopUrl(shopurlUserInput): return shopurlUserInput -####### IP2TOR API CALLS ######### + +##################### +# IP2TOR API CALLS +##################### def apiGetHosts(session, shopurl): - print("# apiGetHosts") - hosts=[] + hosts = [] # make HTTP request + url = "{0}/api/v1/public/hosts/?is_testnet={1}".format(shopurl, int(is_testnet)) try: - url="{0}/api/v1/public/hosts/?is_testnet={1}".format(shopurl, int(is_testnet)) response = session.get(url) except Exception as e: - raise BlitzError("failed HTTP request",url,e) + raise BlitzError("failed HTTP request", url, e) if response.status_code != 200: - raise BlitzError("failed HTTP code",response.status_code,) - + raise BlitzError("failed HTTP code", response.status_code, ) + # parse & validate data try: jData = json.loads(response.content) except Exception as e: - raise BlitzError("failed JSON parsing",response.content,e) + raise BlitzError("failed JSON parsing", response.content, e) if not isinstance(jData, list): - raise BlitzError("hosts not list",response.content) + raise BlitzError("hosts not list", response.content) for idx, hostEntry in enumerate(jData): try: # ignore if not offering tor bridge - if not hostEntry['offers_tor_bridges']: continue + if not hostEntry['offers_tor_bridges']: + continue # ignore if duration is less than an hour - if hostEntry['tor_bridge_duration'] < 3600: continue + if hostEntry['tor_bridge_duration'] < 3600: + continue # add duration per hour value - hostEntry['tor_bridge_duration_hours'] = math.floor(hostEntry['tor_bridge_duration']/3600) + hostEntry['tor_bridge_duration_hours'] = math.floor(hostEntry['tor_bridge_duration'] / 3600) # ignore if prices are negative or below one sat (maybe msats later) - if hostEntry['tor_bridge_price_initial'] < 1000: continue - if hostEntry['tor_bridge_price_extension'] < 1000: continue + if hostEntry['tor_bridge_price_initial'] < 1000: + continue + if hostEntry['tor_bridge_price_extension'] < 1000: + continue # add price in sats - hostEntry['tor_bridge_price_initial_sats'] = math.ceil(hostEntry['tor_bridge_price_initial']/1000) - hostEntry['tor_bridge_price_extension_sats'] = math.ceil(hostEntry['tor_bridge_price_extension']/1000) + hostEntry['tor_bridge_price_initial_sats'] = math.ceil(hostEntry['tor_bridge_price_initial'] / 1000) + hostEntry['tor_bridge_price_extension_sats'] = math.ceil(hostEntry['tor_bridge_price_extension'] / 1000) # ignore name is less then 3 chars - if len(hostEntry['name']) < 3: continue + if len(hostEntry['name']) < 3: + continue # ignore id with zero value - if len(hostEntry['id']) < 1: continue + if len(hostEntry['id']) < 1: + continue # shorten names to 20 chars max hostEntry['name'] = hostEntry['name'][:20] except Exception as e: - raise BlitzError("failed host entry pasring",str(hostEntry),e) + raise BlitzError("failed host entry pasring", str(hostEntry), e) hosts.append(hostEntry) - + print("# found {0} valid torbridge hosts".format(len(hosts))) return hosts -def apiPlaceOrderNew(session, shopurl, hostid, toraddressWithPort): +def apiPlaceOrderNew(session, shopurl, hostid, toraddressWithPort): print("# apiPlaceOrderNew") + url = "{0}/api/v1/public/order/".format(shopurl) + postData = { + 'product': "tor_bridge", + 'host_id': hostid, + 'tos_accepted': True, + 'comment': 'test', + 'target': toraddressWithPort, + 'public_key': '' + } try: - url="{0}/api/v1/public/order/".format(shopurl) - postData={ - 'product': "tor_bridge", - 'host_id': hostid, - 'tos_accepted': True, - 'comment': 'test', - 'target': toraddressWithPort, - 'public_key': '' - } response = session.post(url, data=postData) except Exception as e: - raise BlitzError("failed HTTP request",url,e) + raise BlitzError("failed HTTP request", url, e) if response.status_code == 420: - raise BlitzError("forwarding this address was rejected",response.status_code) + raise BlitzError("forwarding this address was rejected", response.status_code) if response.status_code != 201: - raise BlitzError("failed HTTP code",response.status_code) + raise BlitzError("failed HTTP code", response.status_code) # parse & validate data try: @@ -198,23 +223,23 @@ def apiPlaceOrderNew(session, shopurl, hostid, toraddressWithPort): print("error='MISSING ID'") return except Exception as e: - raise BlitzError("failed JSON parsing",response.status_code,e) + raise BlitzError("failed JSON parsing", response.status_code, e) return jData['id'] -def apiPlaceOrderExtension(session, shopurl, bridgeid): +def apiPlaceOrderExtension(session, shopurl, bridgeid): print("# apiPlaceOrderExtension") + url = "{0}/api/v1/public/tor_bridges/{1}/extend/".format(shopurl, bridgeid) try: - url="{0}/api/v1/public/tor_bridges/{1}/extend/".format(shopurl, bridgeid) response = session.post(url) except Exception as e: - raise BlitzError("failed HTTP request",url,e) + raise BlitzError("failed HTTP request", url, e) if response.status_code == 420: - raise BlitzError("forwarding this address was rejected",response.status_code) + raise BlitzError("forwarding this address was rejected", response.status_code) if response.status_code != 200 and response.status_code != 201: - raise BlitzError("failed HTTP code",response.status_code) + raise BlitzError("failed HTTP code", response.status_code) # parse & validate data print("# parse") @@ -224,59 +249,61 @@ def apiPlaceOrderExtension(session, shopurl, bridgeid): print("error='MISSING ID'") return except Exception as e: - raise BlitzError("failed JSON parsing",response.content, e) + raise BlitzError("failed JSON parsing", response.content, e) return jData['po_id'] def apiGetOrder(session, shopurl, orderid): - print("# apiGetOrder") # make HTTP request + url = "{0}/api/v1/public/pos/{1}/".format(shopurl, orderid) try: - url="{0}/api/v1/public/pos/{1}/".format(shopurl,orderid) response = session.get(url) except Exception as e: - raise BlitzError("failed HTTP request",url, e) + raise BlitzError("failed HTTP request", url, e) if response.status_code != 200: - raise BlitzError("failed HTTP code",response.status_code) + raise BlitzError("failed HTTP code", response.status_code) # parse & validate data try: jData = json.loads(response.content) if len(jData['item_details']) == 0: - raise BlitzError("missing item",response.content) + raise BlitzError("missing item", response.content) if len(jData['ln_invoices']) > 1: - raise BlitzError("more than one invoice",response.content) + raise BlitzError("more than one invoice", response.content) except Exception as e: - raise BlitzError("failed JSON parsing",response.content, e) + raise BlitzError("failed JSON parsing", response.content, e) return jData -def apiGetBridgeStatus(session, shopurl, bridgeid): +def apiGetBridgeStatus(session, shopurl, bridgeid): print("# apiGetBridgeStatus") # make HTTP request + url = "{0}/api/v1/public/tor_bridges/{1}/".format(shopurl, bridgeid) try: - url="{0}/api/v1/public/tor_bridges/{1}/".format(shopurl,bridgeid) response = session.get(url) except Exception as e: - raise BlitzError("failed HTTP request",url, e) + raise BlitzError("failed HTTP request", url, e) if response.status_code != 200: - raise BlitzError("failed HTTP code",response.status_code) + raise BlitzError("failed HTTP code", response.status_code) # parse & validate data try: jData = json.loads(response.content) if len(jData['id']) == 0: - raise BlitzError("missing id",response.content) + raise BlitzError("missing id", response.content) except Exception as e: - raise BlitzError("failed JSON parsing",response.content, e) + raise BlitzError("failed JSON parsing", response.content, e) return jData -####### LND API CALLS ######### + +##################### +# LND API CALLS +##################### def lndDecodeInvoice(lnInvoiceString): try: @@ -294,13 +321,14 @@ def lndDecodeInvoice(lnInvoiceString): # validate results if response.num_msat <= 0: - raise BlitzError("zero invoice not allowed",lnInvoiceString) + raise BlitzError("zero invoice not allowed", lnInvoiceString) except Exception as e: - raise BlitzError("failed LND invoice decoding",lnInvoiceString,e) + raise BlitzError("failed LND invoice decoding", lnInvoiceString, e) return response + def lndPayInvoice(lnInvoiceString): try: # call LND GRPC API @@ -317,36 +345,38 @@ def lndPayInvoice(lnInvoiceString): # validate results if len(response.payment_error) > 0: - raise BlitzError(response.payment_error,lnInvoiceString) + raise BlitzError(response.payment_error, lnInvoiceString) except Exception as e: - raise BlitzError("payment failed",lnInvoiceString,e) + raise BlitzError("payment failed", lnInvoiceString, e) return response -####### PROCESS FUNCTIONS ######### + +##################### +# PROCESS FUNCTIONS +##################### def shopList(shopUrl): - print("#### Getting available options from shop ...") - shopUrl=normalizeShopUrl(shopUrl) + shopUrl = normalizeShopUrl(shopUrl) return apiGetHosts(session, shopUrl) -def shopOrder(shopUrl, hostid, servicename, torTarget, duration, msatsFirst, msatsNext, description=""): +def shopOrder(shopUrl, hostid, servicename, torTarget, duration, msatsFirst, msatsNext, description=""): print("#### Placeing order ...") - shopUrl=normalizeShopUrl(shopUrl) + shopUrl = normalizeShopUrl(shopUrl) orderid = apiPlaceOrderNew(session, shopUrl, hostid, torTarget) print("#### Waiting until invoice is available ...") - loopCount=0 + loopCount = 0 while True: time.sleep(2) - loopCount+=1 + loopCount += 1 print("# Loop {0}".format(loopCount)) order = apiGetOrder(session, shopUrl, orderid) if order['status'] == "R": - raise BlitzError("Subscription Rejected",order) + raise BlitzError("Subscription Rejected", order) if len(order['ln_invoices']) > 0 and order['ln_invoices'][0]['payment_request'] is not None: break if loopCount > 60: @@ -365,24 +395,25 @@ def shopOrder(shopUrl, hostid, servicename, torTarget, duration, msatsFirst, msa print("# amount as advertised: {0} milliSats".format(msatsFirst)) print("# amount in invoice is: {0} milliSats".format(paymentRequestDecoded.num_msat)) if int(msatsFirst) < int(paymentRequestDecoded.num_msat): - raise BlitzError("invoice bigger amount than advertised", "advertised({0}) invoice({1})".format(msatsFirst, paymentRequestDecoded.num_msat)) + raise BlitzError("invoice bigger amount than advertised", + "advertised({0}) invoice({1})".format(msatsFirst, paymentRequestDecoded.num_msat)) print("#### Paying invoice ...") payedInvoice = lndPayInvoice(paymentRequestStr) print('# OK PAYMENT SENT') print("#### Waiting until bridge is ready ...") - loopCount=0 + loopCount = 0 while True: time.sleep(3) - loopCount+=1 + loopCount += 1 print("## Loop {0}".format(loopCount)) bridge = apiGetBridgeStatus(session, shopUrl, bridge_id) if bridge['status'] == "A": break - if loopCount > 120: + if loopCount > 120: raise BlitzError("timeout bridge not getting ready", bridge) - + print("#### Check if port is valid ...") if not bridge['port'].isdigit(): raise BlitzError("invalid port", bridge) @@ -390,14 +421,14 @@ def shopOrder(shopUrl, hostid, servicename, torTarget, duration, msatsFirst, msa print("#### Check if duration delivered is as advertised ...") contract_breached = False warning_text = "" - secondsDelivered=secondsLeft(parseDate(bridge['suspend_after'])) + secondsDelivered = secondsLeft(parseDate(bridge['suspend_after'])) print("# delivered({0}) promised({1})".format(secondsDelivered, duration)) if (secondsDelivered + 600) < int(duration): contract_breached = True warning_text = "delivered duration shorter than advertised" # create subscription data for storage - subscription = {} + subscription = dict() subscription['type'] = "ip2tor-v1" subscription['id'] = bridge['id'] subscription['name'] = servicename @@ -409,8 +440,8 @@ def shopOrder(shopUrl, hostid, servicename, torTarget, duration, msatsFirst, msa subscription['price_initial'] = int(msatsFirst) subscription['price_extension'] = int(msatsNext) subscription['price_total'] = int(paymentRequestDecoded.num_msat) - subscription['time_created'] = str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M")) - subscription['time_lastupdate'] = str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M")) + subscription['time_created'] = str(datetime.now().strftime("%Y-%m-%d %H:%M")) + subscription['time_lastupdate'] = str(datetime.now().strftime("%Y-%m-%d %H:%M")) subscription['suspend_after'] = bridge['suspend_after'] subscription['description'] = str(description) subscription['contract_breached'] = contract_breached @@ -436,33 +467,32 @@ def shopOrder(shopUrl, hostid, servicename, torTarget, duration, msatsFirst, msa except Exception as e: eprint(e) - raise BlitzError("fail on subscription storage",subscription, e) + raise BlitzError("fail on subscription storage", str(subscription), e) print("# OK - BRIDGE READY: {0}:{1} -> {2}".format(bridge_ip, subscription['port'], torTarget)) return subscription - -def subscriptionExtend(shopUrl, bridgeid, durationAdvertised, msatsNext, bridge_suspendafter): - warningTXT="" - contract_breached=False + +def subscriptionExtend(shopUrl, bridgeid, durationAdvertised, msatsNext, bridge_suspendafter): + warningTXT = "" + contract_breached = False print("#### Placing extension order ...") - shopUrl=normalizeShopUrl(shopUrl) + shopUrl = normalizeShopUrl(shopUrl) orderid = apiPlaceOrderExtension(session, shopUrl, bridgeid) print("#### Waiting until invoice is available ...") - loopCount=0 + loopCount = 0 while True: time.sleep(2) - loopCount+=1 + loopCount += 1 print("## Loop {0}".format(loopCount)) order = apiGetOrder(session, shopUrl, orderid) if len(order['ln_invoices']) > 0 and order['ln_invoices'][0]['payment_request'] is not None: break if loopCount > 120: raise BlitzError("timeout on getting invoice", order) - - + paymentRequestStr = order['ln_invoices'][0]['payment_request'] print("#### Decoding invoice and checking ..)") @@ -472,18 +502,20 @@ def subscriptionExtend(shopUrl, bridgeid, durationAdvertised, msatsNext, bridge_ print("# amount as advertised: {0} milliSats".format(msatsNext)) print("# amount in invoice is: {0} milliSats".format(paymentRequestDecoded.num_msat)) if int(msatsNext) < int(paymentRequestDecoded.num_msat): - raise BlitzError("invoice bigger amount than advertised", "advertised({0}) invoice({1})".format(msatsNext, paymentRequestDecoded.num_msat)) + raise BlitzError("invoice bigger amount than advertised", + "advertised({0}) invoice({1})".format(msatsNext, paymentRequestDecoded.num_msat)) print("#### Paying invoice ...") payedInvoice = lndPayInvoice(paymentRequestStr) print("#### Check if bridge was extended ...") - loopCount=0 + bridge = None + loopCount = 0 while True: time.sleep(3) - loopCount+=1 + loopCount += 1 print("## Loop {0}".format(loopCount)) - try: + try: bridge = apiGetBridgeStatus(session, shopUrl, bridgeid) if bridge['suspend_after'] != bridge_suspendafter: break @@ -491,11 +523,11 @@ def subscriptionExtend(shopUrl, bridgeid, durationAdvertised, msatsNext, bridge_ eprint(e) print("# EXCEPTION on apiGetBridgeStatus") if loopCount > 240: - warningTXT="timeout on last payed extension" - contract_breached=True + warningTXT = "timeout on last payed extension" + contract_breached = True break - if not contract_breached: + if bridge and not contract_breached: print("#### Check if extension duration is as advertised ...") secondsLeftOld = secondsLeft(parseDate(bridge_suspendafter)) secondsLeftNew = secondsLeft(parseDate(bridge['suspend_after'])) @@ -513,7 +545,7 @@ def subscriptionExtend(shopUrl, bridgeid, durationAdvertised, msatsNext, bridge_ for idx, subscription in enumerate(subscriptions['subscriptions_ip2tor']): if subscription['id'] == bridgeid: subscription['suspend_after'] = str(bridge['suspend_after']) - subscription['time_lastupdate'] = str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M")) + subscription['time_lastupdate'] = str(datetime.now().strftime("%Y-%m-%d %H:%M")) subscription['price_total'] += int(paymentRequestDecoded.num_msat) subscription['contract_breached'] = contract_breached subscription['warning'] = warningTXT @@ -523,14 +555,17 @@ def subscriptionExtend(shopUrl, bridgeid, durationAdvertised, msatsNext, bridge_ writer.write(toml.dumps(subscriptions)) writer.close() break - + except Exception as e: eprint(e) - raise BlitzError("fail on subscription storage",subscription, e) + raise BlitzError("fail on subscription storage", "", e) print("# BRIDGE GOT EXTENDED: {0} -> {1}".format(bridge_suspendafter, bridge['suspend_after'])) + def menuMakeSubscription(blitzServiceName, torAddress, torPort): + # late imports - so that rest of script can run also if dependency is not available + from dialog import Dialog torTarget = "{0}:{1}".format(torAddress, torPort) @@ -549,7 +584,7 @@ def menuMakeSubscription(blitzServiceName, torAddress, torPort): while True: # input shop url - d = Dialog(dialog="dialog",autowidgetsize=True) + d = Dialog(dialog="dialog", autowidgetsize=True) d.set_background_title("Select IP2TOR Bridge Shop (communication secured thru TOR)") code, text = d.inputbox( "Enter Address of a IP2TOR Shop (OR USE DEFAULT):", @@ -557,7 +592,8 @@ def menuMakeSubscription(blitzServiceName, torAddress, torPort): title="Shop Address") # if user canceled - if code != d.OK: sys.exit(0) + if code != d.OK: + sys.exit(0) # get host list from shop shopurl = text @@ -566,38 +602,47 @@ def menuMakeSubscription(blitzServiceName, torAddress, torPort): hosts = shopList(shopurl) except Exception as e: # shopurl not working - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' Cannot reach a shop under that address. Please check domain or cancel dialog. - ''',title="ERROR") + ''', title="ERROR") else: # when shop is empty if len(hosts) == 0: - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' The shop has no available offers at the moment. Try again later, enter another address or cancel. - ''',title="ERROR") + ''', title="ERROR") # ok we got hosts - continue - else: break + else: + break ############################### # PHASE 2: SELECT SUBSCRIPTION # create menu to select shop - host=None + host = None choices = [] for idx, hostEntry in enumerate(hosts): - choices.append( ("{0}".format(idx), "{0} ({1} hours, first: {2} sats, next: {3} sats)".format(hostEntry['name'].ljust(20), hostEntry['tor_bridge_duration_hours'], hostEntry['tor_bridge_price_initial_sats'], hostEntry['tor_bridge_price_extension_sats'])) ) - + choices.append( + ("{0}".format(idx), + "{0} ({1} hours, first: {2} sats, next: {3} sats)".format( + hostEntry['name'].ljust(20), + hostEntry['tor_bridge_duration_hours'], + hostEntry['tor_bridge_price_initial_sats'], + hostEntry['tor_bridge_price_extension_sats']) + ) + ) + while True: # show menu with options - d = Dialog(dialog="dialog",autowidgetsize=True) + d = Dialog(dialog="dialog", autowidgetsize=True) d.set_background_title("IP2TOR Bridge Shop: {0}".format(shopurl)) code, tag = d.menu( "Following TOR bridge hosts are available. Select for details:", choices=choices, title="Available Subscriptions") - + # if user cancels if code != d.OK: sys.exit(0) @@ -610,9 +655,9 @@ Try again later, enter another address or cancel. if len(host['terms_of_service_url']) == 0: host['terms_of_service_url'] = "-" # show details of selected - d = Dialog(dialog="dialog",autowidgetsize=True) + d = Dialog(dialog="dialog", autowidgetsize=True) d.set_background_title("IP2TOR Bridge Offer Details: {0}".format(shopurl)) - text=''' + text = ''' The subscription would renew every {0} hours. The first time it would cost: {1} sats Every next time it would cost: {2} sats @@ -633,18 +678,19 @@ The service has the following additional terms: More information on the service you can find under: {6} '''.format( - host['tor_bridge_duration_hours'], - host['tor_bridge_price_initial_sats'], - host['tor_bridge_price_extension_sats'], - host['ip'], - torTarget, - host['terms_of_service'], - host['terms_of_service_url'], - blitzServiceName + host['tor_bridge_duration_hours'], + host['tor_bridge_price_initial_sats'], + host['tor_bridge_price_extension_sats'], + host['ip'], + torTarget, + host['terms_of_service'], + host['terms_of_service_url'], + blitzServiceName ) - code = d.msgbox(text, title=host['name'], ok_label="Back", extra_button=True, extra_label="AGREE" ,width=75, height=30) - + code = d.msgbox(text, title=host['name'], ok_label="Back", extra_button=True, extra_label="AGREE", width=75, + height=30) + # if user AGREED break loop and continue with selected host if code == "extra": break @@ -656,33 +702,34 @@ More information on the service you can find under: try: os.system('clear') - subscription = shopOrder(shopurl, host['id'], blitzServiceName, torTarget, host['tor_bridge_duration'], host['tor_bridge_price_initial'],host['tor_bridge_price_extension'],description) + subscription = shopOrder(shopurl, host['id'], blitzServiceName, torTarget, host['tor_bridge_duration'], + host['tor_bridge_price_initial'], host['tor_bridge_price_extension'], description) except BlitzError as be: - exitcode=0 + exitcode = 0 - if (be.errorShort == "timeout on waiting for extending bridge" or - be.errorShort == "fail on subscription storage" or - be.errorShort == "invalid port" or - be.errorShort == "timeout bridge not getting ready") : + if (be.errorShort == "timeout on waiting for extending bridge" or + be.errorShort == "fail on subscription storage" or + be.errorShort == "invalid port" or + be.errorShort == "timeout bridge not getting ready"): - # error happend after payment - exitcode=Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + # error happened after payment + exitcode = Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' You DID PAY the initial fee. But the service was not able to provide service. Subscription will be ignored. Error: {0} - '''.format(be.errorShort),title="Error on Subscription",extra_button=True,extra_label="Details") + '''.format(be.errorShort), title="Error on Subscription", extra_button=True, extra_label="Details") else: - # error happend before payment - exitcode=Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + # error happened before payment + exitcode = Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' You DID NOT PAY the initial fee. The service was not able to provide service. Subscription will be ignored. Error: {0} - '''.format(be.errorShort),title="Error on Subscription",extra_button=True,extra_label="Details") + '''.format(be.errorShort), title="Error on Subscription", extra_button=True, extra_label="Details") # show more details (when user used extra button) if exitcode == Dialog.EXTRA: @@ -699,12 +746,12 @@ Error: {0} print(be.errorLong) print("") input("Press Enter to continue ...") - + sys.exit(1) except Exception as e: - # unkown error happend + # unknown error happened os.system('clear') print('###### EXCEPTION DETAIL FOR DEBUG #######') print("") @@ -718,7 +765,7 @@ Error: {0} input("Press Enter to continue ...") sys.exit(1) - # if LND REST or LND GRPS service ... add bridge IP to TLS + # if LND REST or LND GRPC service ... add bridge IP to TLS if servicename == "LND-REST-API" or servicename == "LND-GRPC-API": os.system("sudo /home/admin/config.scripts/lnd.tlscert.sh ip-add {0}".format(subscription['ip'])) os.system("sudo /home/admin/config.scripts/lnd.credentials.sh reset tls") @@ -726,19 +773,19 @@ Error: {0} # warn user if not delivered as advertised if subscription['contract_breached']: - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' The service was payed & delivered, but RaspiBlitz detected: {0} You may want to consider to cancel the subscription later. - '''.format(subscription['warning'],title="Warning")) + '''.format(subscription['warning'], title="Warning")) # decide if https:// address - protocol="" + protocol = "" if blitzServiceName == "LNBITS": - protocol="https://" + protocol = "https://" # Give final result feedback to user - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' OK your service is ready & your subscription is active. You payed {0} sats for the first {1} hours. @@ -758,11 +805,13 @@ MAIN MENU > Manage Subscriptions > My Subscriptions subscription['ip'], subscription['port'], protocol), - title="Subscription Active" - ) + title="Subscription Active" + ) -####### COMMANDS ######### +##################### +# COMMANDS +##################### ############### # CREATE SSH DIALOG @@ -773,15 +822,15 @@ if sys.argv[1] == "create-ssh-dialog": # check parameters try: - if len(sys.argv) <= 4: raise BlitzError("incorrect parameters","") - servicename = sys.argv[2] - toraddress = sys.argv[3] - port = sys.argv[4] + if len(sys.argv) <= 4: + raise BlitzError("incorrect parameters", "") except Exception as e: handleException(e) - # late imports - so that rest of script can run also if dependency is not available - from dialog import Dialog + servicename = sys.argv[2] + toraddress = sys.argv[3] + port = sys.argv[4] + menuMakeSubscription(servicename, toraddress, port) sys.exit() @@ -795,58 +844,61 @@ if sys.argv[1] == "shop-list": # check parameters try: - if len(sys.argv) <= 2: raise BlitzError("incorrect parameters","") - shopurl = sys.argv[2] + if len(sys.argv) <= 2: + raise BlitzError("incorrect parameters", "") except Exception as e: handleException(e) - # get data + shopurl = sys.argv[2] + try: + # get data hosts = shopList(shopurl) + # output is json list of hosts + print(json.dumps(hosts, indent=2)) except Exception as e: handleException(e) - # output is json list of hosts - print(json.dumps(hosts, indent=2)) sys.exit(0) -############### +########################## # SHOP ORDER # call from web interface -############### +########################## if sys.argv[1] == "shop-order": # check parameters try: - if len(sys.argv) <= 8: raise BlitzError("incorrect parameters","") - shopurl = sys.argv[2] - servicename = sys.argv[3] - hostid = sys.argv[4] - toraddress = sys.argv[5] - duration = sys.argv[6] - msatsFirst = sys.argv[7] - msatsNext = sys.argv[8] - if len(sys.argv) >=10: - description = sys.argv[9] - else: - description = "" + if len(sys.argv) <= 8: + raise BlitzError("incorrect parameters", "") except Exception as e: handleException(e) + shopurl = sys.argv[2] + servicename = sys.argv[3] + hostid = sys.argv[4] + toraddress = sys.argv[5] + duration = sys.argv[6] + msatsFirst = sys.argv[7] + msatsNext = sys.argv[8] + if len(sys.argv) >= 10: + description = sys.argv[9] + else: + description = "" + # get data try: subscription = shopOrder(shopurl, hostid, servicename, toraddress, duration, msatsFirst, msatsNext, description) + # output json ordered bridge + print(json.dumps(subscription, indent=2)) + sys.exit() except Exception as e: handleException(e) - # output json ordered bridge - print(json.dumps(subscription, indent=2)) - sys.exit() - ####################### # SUBSCRIPTIONS LIST -# call in intervalls from background process +# call in intervals from background process ####################### if sys.argv[1] == "subscriptions-list": @@ -861,7 +913,7 @@ if sys.argv[1] == "subscriptions-list": if "subscriptions_ip2tor" not in subs: subs['subscriptions_ip2tor'] = [] print(json.dumps(subs['subscriptions_ip2tor'], indent=2)) - + except Exception as e: handleException(e) @@ -869,7 +921,7 @@ if sys.argv[1] == "subscriptions-list": ####################### # SUBSCRIPTIONS RENEW -# call in intervalls from background process +# call in intervals from background process ####################### if sys.argv[1] == "subscriptions-renew": @@ -879,19 +931,20 @@ if sys.argv[1] == "subscriptions-renew": # check parameters try: secondsBeforeSuspend = int(sys.argv[2]) - if secondsBeforeSuspend < 0: secondsBeforeSuspend = 0 + if secondsBeforeSuspend < 0: + secondsBeforeSuspend = 0 except Exception as e: print("# running with secondsBeforeSuspend=0") secondsBeforeSuspend = 0 - # check if any active subscrpitions are below the secondsBeforeSuspend - if yes extend - + # check if any active subscriptions are below the secondsBeforeSuspend - if yes extend + try: - + if not Path(SUBSCRIPTIONS_FILE).is_file(): print("# no subscriptions") sys.exit(0) - + os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) subscriptions = toml.load(SUBSCRIPTIONS_FILE) for idx, subscription in enumerate(subscriptions['subscriptions_ip2tor']): @@ -901,26 +954,28 @@ if sys.argv[1] == "subscriptions-renew": if subscription['active'] and subscription['type'] == "ip2tor-v1": secondsToRun = secondsLeft(parseDate(subscription['suspend_after'])) if secondsToRun < secondsBeforeSuspend: - print("# RENEW: subscription {0} with {1} seconds to run".format(subscription['id'],secondsToRun)) + print("# RENEW: subscription {0} with {1} seconds to run".format(subscription['id'], + secondsToRun)) subscriptionExtend( - subscription['shop'], - subscription['id'], - subscription['duration'], + subscription['shop'], + subscription['id'], + subscription['duration'], subscription['price_extension'], subscription['suspend_after'] - ) + ) else: - print("# GOOD: subscription {0} with {1} seconds to run".format(subscription['id'],secondsToRun)) + print("# GOOD: subscription {0} with {1} " + "seconds to run".format(subscription['id'], secondsToRun)) except BlitzError as be: # write error into subscription warning subs = toml.load(SUBSCRIPTIONS_FILE) - for idx, sub in enumerate(subs['subscriptions_ip2tor']): + for sub in subs['subscriptions_ip2tor']: if sub['id'] == subscription['id']: sub['warning'] = "Exception on Renew: {0}".format(be.errorShort) if be.errorShort == "invoice bigger amount than advertised": - sub['contract_breached'] = True - sub['active'] = False + sub['contract_breached'] = True + sub['active'] = False with open(SUBSCRIPTIONS_FILE, 'w') as writer: writer.write(toml.dumps(subs)) writer.close() @@ -931,7 +986,7 @@ if sys.argv[1] == "subscriptions-renew": except Exception as e: print("# EXCEPTION on subscriptions-renew of subscription index {0}".format(idx)) eprint(e) - + except Exception as e: handleException(e) @@ -947,13 +1002,14 @@ if sys.argv[1] == "subscription-cancel": # check parameters try: - if len(sys.argv) <= 2: raise BlitzError("incorrect parameters","") - subscriptionID = sys.argv[2] + if len(sys.argv) <= 2: + raise BlitzError("incorrect parameters", "") except Exception as e: handleException(e) - try: + subscriptionID = sys.argv[2] + try: os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) subs = toml.load(SUBSCRIPTIONS_FILE) newList = [] @@ -983,11 +1039,13 @@ if sys.argv[1] == "subscription-by-service": # check parameters try: - if len(sys.argv) <= 2: raise BlitzError("incorrect parameters","") - servicename = sys.argv[2] + if len(sys.argv) <= 2: + raise BlitzError("incorrect parameters", "") except Exception as e: handleException(e) + servicename = sys.argv[2] + try: if os.path.isfile(SUBSCRIPTIONS_FILE): os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) @@ -999,7 +1057,7 @@ if sys.argv[1] == "subscription-by-service": print("port='{0}'".format(sub['port'])) print("tor='{0}'".format(sub['tor'])) sys.exit(0) - + print("error='not found'") sys.exit(0) @@ -1017,11 +1075,13 @@ if sys.argv[1] == "ip-by-tor": # check parameters try: - if len(sys.argv) <= 2: raise BlitzError("incorrect parameters","") - onion = sys.argv[2] + if len(sys.argv) <= 2: + raise BlitzError("incorrect parameters", "") except Exception as e: handleException(e) + onion = sys.argv[2] + try: if os.path.isfile(SUBSCRIPTIONS_FILE): os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) @@ -1034,7 +1094,7 @@ if sys.argv[1] == "ip-by-tor": print("port='{0}'".format(sub['port'])) print("tor='{0}'".format(sub['tor'])) sys.exit(0) - + print("error='not found'") sys.exit(0) @@ -1043,5 +1103,5 @@ if sys.argv[1] == "ip-by-tor": sys.exit(1) -# unkown command -print("# unkown command") +# unknown command +print("# unknown command") diff --git a/home.admin/config.scripts/blitz.subscriptions.letsencrypt.py b/home.admin/config.scripts/blitz.subscriptions.letsencrypt.py index b6c23a95e..6f829f8a0 100644 --- a/home.admin/config.scripts/blitz.subscriptions.letsencrypt.py +++ b/home.admin/config.scripts/blitz.subscriptions.letsencrypt.py @@ -1,19 +1,20 @@ #!/usr/bin/python3 -import sys -import locale -import requests import json -import math -import time -import datetime, time +import os import subprocess -import codecs, grpc, os +import sys +import time +from datetime import datetime from pathlib import Path + +import requests import toml from blitzpy import RaspiBlitzConfig -####### SCRIPT INFO ######### +##################### +# SCRIPT INFO +##################### # - this subscription does not require any payments # - the recurring part is managed by the lets encrypt ACME script @@ -29,9 +30,11 @@ if len(sys.argv) <= 1 or sys.argv[1] == "-h" or sys.argv[1] == "help": print("# blitz.subscriptions.ip2tor.py subscription-cancel ") sys.exit(1) -####### BASIC SETTINGS ######### +##################### +# BASIC SETTINGS +##################### -SUBSCRIPTIONS_FILE="/mnt/hdd/app-data/subscriptions/subscriptions.toml" +SUBSCRIPTIONS_FILE = "/mnt/hdd/app-data/subscriptions/subscriptions.toml" cfg = RaspiBlitzConfig() cfg.reload() @@ -39,9 +42,12 @@ cfg.reload() # todo: make sure that also ACME script uses TOR if activated session = requests.session() if cfg.run_behind_tor: - session.proxies = {'http': 'socks5h://127.0.0.1:9050', 'https': 'socks5h://127.0.0.1:9050'} + session.proxies = {'http': 'socks5h://127.0.0.1:9050', 'https': 'socks5h://127.0.0.1:9050'} -####### HELPER CLASSES ######### + +##################### +# HELPER CLASSES +##################### class BlitzError(Exception): def __init__(self, errorShort, errorLong="", errorException=None): @@ -49,11 +55,15 @@ class BlitzError(Exception): self.errorLong = str(errorLong) self.errorException = errorException -####### HELPER FUNCTIONS ######### + +##################### +# HELPER FUNCTIONS +##################### def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) + def handleException(e): if isinstance(e, BlitzError): eprint(e.errorLong) @@ -64,30 +74,35 @@ def handleException(e): print("error='{0}'".format(str(e))) sys.exit(1) + def getsubdomain(fulldomainstring): return fulldomainstring.split('.')[0] -####### API Calls to DNS Servcies ######### + +############################ +# API Calls to DNS Servcies +############################ def duckDNSupdate(domain, token, ip): - print("# duckDNS update IP API call for {0}".format(domain)) - + # make HTTP request + url = "https://www.duckdns.org/update?domains={0}&token={1}&ip={2}".format(getsubdomain(domain), token, ip) try: - url="https://www.duckdns.org/update?domains={0}&token={1}&ip={2}".format(getsubdomain(domain), token, ip) response = session.get(url) + if response.status_code != 200: + raise BlitzError("failed HTTP code", str(response.status_code)) except Exception as e: - raise BlitzError("failed HTTP request",url,e) - if response.status_code != 200: - raise BlitzError("failed HTTP code",response.status_code) - + raise BlitzError("failed HTTP request", url, e) + return response.content -####### PROCESS FUNCTIONS ######### + +##################### +# PROCESS FUNCTIONS +##################### def subscriptionsNew(ip, dnsservice, id, token, target): - # id needs to the full domain name if id.find(".") == -1: raise BlitzError("not a fully qualified domainname", dnsservice_id) @@ -100,13 +115,14 @@ def subscriptionsNew(ip, dnsservice, id, token, target): os.system("/home/admin/config.scripts/bonus.letsencrypt.sh on") # dyndns - realip=ip + realip = ip if ip == "dyndns": - updateURL="" + updateURL = "" if dnsservice == "duckdns": - updateURL=="https://www.duckdns.org/update?domains={0}&token={1}".format(getsubdomain(domain), token, ip) - subprocess.run(['/home/admin/config.scriprs/internet.dyndomain.sh', 'on', id, updateURL], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() - realip=cfg.public_ip + updateURL = "https://www.duckdns.org/update?domains={0}&token={1}".format(getsubdomain(domain), token, ip) + subprocess.run(['/home/admin/config.scriprs/internet.dyndomain.sh', 'on', id, updateURL], + stdout=subprocess.PIPE).stdout.decode('utf-8').strip() + realip = cfg.public_ip # update DNS with actual IP if dnsservice == "duckdns": @@ -114,14 +130,16 @@ def subscriptionsNew(ip, dnsservice, id, token, target): # run the ACME script print("# Running letsencrypt ACME script ...") - acmeResult=subprocess.Popen(["/home/admin/config.scripts/bonus.letsencrypt.sh", "issue-cert", dnsservice, id, token, target], stdout=subprocess.PIPE, stderr = subprocess.STDOUT, encoding='utf8') + acmeResult = subprocess.Popen( + ["/home/admin/config.scripts/bonus.letsencrypt.sh", "issue-cert", dnsservice, id, token, target], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8') out, err = acmeResult.communicate() if out.find("error=") > -1: time.sleep(6) raise BlitzError("letsancrypt acme failed", out) # create subscription data for storage - subscription = {} + subscription = dict() subscription['type'] = "letsencrypt-v1" subscription['id'] = id subscription['active'] = True @@ -131,7 +149,7 @@ def subscriptionsNew(ip, dnsservice, id, token, target): subscription['ip'] = ip subscription['target'] = target subscription['description'] = "For {0}".format(target) - subscription['time_created'] = str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M")) + subscription['time_created'] = str(datetime.now().strftime("%Y-%m-%d %H:%M")) subscription['warning'] = "" # load, add and store subscriptions @@ -152,31 +170,35 @@ def subscriptionsNew(ip, dnsservice, id, token, target): except Exception as e: eprint(e) - raise BlitzError("fail on subscription storage",subscription, e) + raise BlitzError("fail on subscription storage", str(subscription), e) print("# OK - LETSENCRYPT DOMAIN IS READY") return subscription + def subscriptionsCancel(id): + # ToDo(frennkie) id is not used.. os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) subs = toml.load(SUBSCRIPTIONS_FILE) newList = [] - removedCert = False + removedCert = None for idx, sub in enumerate(subs['subscriptions_letsencrypt']): if sub['id'] != subscriptionID: newList.append(sub) else: - removedCert=sub + removedCert = sub subs['subscriptions_letsencrypt'] = newList # run the ACME script to remove cert if removedCert: - acmeResult=subprocess.Popen(["/home/admin/config.scripts/bonus.letsencrypt.sh", "remove-cert", removedCert['id'], removedCert['target']], stdout=subprocess.PIPE, stderr = subprocess.STDOUT, encoding='utf8') + acmeResult = subprocess.Popen( + ["/home/admin/config.scripts/bonus.letsencrypt.sh", "remove-cert", removedCert['id'], + removedCert['target']], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8') out, err = acmeResult.communicate() if out.find("error=") > -1: time.sleep(6) - raise BlitzError("letsancrypt acme failed", out) + raise BlitzError("letsencrypt acme failed", out) # persist change with open(SUBSCRIPTIONS_FILE, 'w') as writer: @@ -187,8 +209,8 @@ def subscriptionsCancel(id): # todo: deinstall letsencrypt if this was last subscription -def getSubscription(subscriptionID): +def getSubscription(subscriptionID): try: if Path(SUBSCRIPTIONS_FILE).is_file(): @@ -202,13 +224,12 @@ def getSubscription(subscriptionID): if sub['id'] == subscriptionID: return sub return [] - + except Exception as e: return [] def getDomainByIP(ip): - # does subscriptin file exists if Path(SUBSCRIPTIONS_FILE).is_file(): os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) @@ -231,6 +252,8 @@ def getDomainByIP(ip): def menuMakeSubscription(): + # late imports - so that rest of script can run also if dependency is not available + from dialog import Dialog # todo ... copy parts of IP2TOR dialogs @@ -239,9 +262,9 @@ def menuMakeSubscription(): # ask user for which RaspiBlitz service the bridge should be used choices = [] - choices.append( ("DUCKDNS", "Use duckdns.org") ) + choices.append(("DUCKDNS", "Use duckdns.org")) - d = Dialog(dialog="dialog",autowidgetsize=True) + d = Dialog(dialog="dialog", autowidgetsize=True) d.set_background_title("LetsEncrypt Subscription") code, tag = d.menu( "\nChoose a free DNS service to work with:", @@ -252,7 +275,7 @@ def menuMakeSubscription(): sys.exit(0) # get the fixed dnsservice string - dnsservice=tag.lower() + dnsservice = tag.lower() ############################ # PHASE 2: Enter ID & API token for service @@ -260,18 +283,18 @@ def menuMakeSubscription(): if dnsservice == "duckdns": # show basic info on duck dns - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' If you havent already go to https://duckdns.org - consider using the TOR browser - create an account or login - make sure you have a subdomain added - ''',title="DuckDNS Account needed") + ''', title="DuckDNS Account needed") # enter the subdomain code, text = d.inputbox( - "Enter yor duckDNS subdomain:", - height=10, width=40, init="", - title="DuckDNS Domain") + "Enter yor duckDNS subdomain:", + height=10, width=40, init="", + title="DuckDNS Domain") subdomain = text.strip() subdomain = subdomain.split(' ')[0] subdomain = getsubdomain(subdomain) @@ -280,16 +303,16 @@ If you havent already go to https://duckdns.org # check for valid input if len(subdomain) == 0: - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' This looks not like a valid subdomain. - ''',title="Unvalid Input") + ''', title="Unvalid Input") sys.exit(0) # enter the token code, text = d.inputbox( - "Enter the duckDNS token of your account:", - height=10, width=50, init="", - title="DuckDNS Token") + "Enter the duckDNS token of your account:", + height=10, width=50, init="", + title="DuckDNS Token") token = text.strip() token = token.split(' ')[0] @@ -297,29 +320,29 @@ This looks not like a valid subdomain. try: token.index("-") except Exception as e: - token="" + token = "" if len(token) < 20: - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' This looks not like a valid token. - ''',title="Unvalid Input") + ''', title="Unvalid Input") sys.exit(0) else: os.system("clear") print("Not supported yet: {0}".format(dnsservice)) time.sleep(4) - sys.exit(0) + sys.exit(0) - ############################ + ############################ # PHASE 3: Choose what kind of IP: dynDNS, IP2TOR, fixedIP # ask user for which RaspiBlitz service the bridge should be used - choices = [] - choices.append( ("IP2TOR", "HTTPS for a IP2TOR Bridge") ) - choices.append( ("DYNDNS", "HTTPS for {0} DynamicIP DNS".format(dnsservice.upper())) ) - choices.append( ("STATIC", "HTTPS for a static IP") ) + choices = list() + choices.append(("IP2TOR", "HTTPS for a IP2TOR Bridge")) + choices.append(("DYNDNS", "HTTPS for {0} DynamicIP DNS".format(dnsservice.upper()))) + choices.append(("STATIC", "HTTPS for a static IP")) - d = Dialog(dialog="dialog",autowidgetsize=True) + d = Dialog(dialog="dialog", autowidgetsize=True) d.set_background_title("LetsEncrypt Subscription") code, tag = d.menu( "\nChoose the kind of IP you want to use:", @@ -331,33 +354,34 @@ This looks not like a valid token. sys.exit(0) # default target are the nginx ip ports - target="ip" + target = "ip" + ip = "" if tag == "IP2TOR": # get all active IP2TOR subscriptions (just in case) - ip2torSubs=[] + ip2torSubs = [] if Path(SUBSCRIPTIONS_FILE).is_file(): os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) subs = toml.load(SUBSCRIPTIONS_FILE) for idx, sub in enumerate(subs['subscriptions_ip2tor']): if sub['active']: ip2torSubs.append(sub) - + # when user has no IP2TOR subs yet if len(ip2torSubs) == 0: - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' You have no active IP2TOR subscriptions. Create one first and try again. - ''',title="No IP2TOR available") - sys.exit(0) + ''', title="No IP2TOR available") + sys.exit(0) - # let user select a IP2TOR subscription + # let user select a IP2TOR subscription choices = [] for idx, sub in enumerate(ip2torSubs): - choices.append( ("{0}".format(idx), "IP2TOR {0} {1}:{2}".format(sub['name'], sub['ip'], sub['port'])) ) - - d = Dialog(dialog="dialog",autowidgetsize=True) + choices.append(("{0}".format(idx), "IP2TOR {0} {1}:{2}".format(sub['name'], sub['ip'], sub['port']))) + + d = Dialog(dialog="dialog", autowidgetsize=True) d.set_background_title("LetsEncrypt Subscription") code, tag = d.menu( "\nChoose the IP2TOR subscription:", @@ -368,22 +392,22 @@ Create one first and try again. sys.exit(0) # get the slected IP2TOR bridge - ip2torSelect=ip2torSubs[int(tag)] - ip=ip2torSelect["ip"] - target="tor" + ip2torSelect = ip2torSubs[int(tag)] + ip = ip2torSelect["ip"] + target = "tor" elif tag == "DYNDNS": # the subscriptioNew method will handle acrivating the dnydns part - ip="dyndns" + ip = "dyndns" elif tag == "STATIC": # enter the static IP code, text = d.inputbox( - "Enter the static public IP of this RaspiBlitz:", - height=10, width=40, init="", - title="Static IP") + "Enter the static public IP of this RaspiBlitz:", + height=10, width=40, init="", + title="Static IP") ip = text.strip() ip = token.split(' ')[0] @@ -391,36 +415,39 @@ Create one first and try again. try: ip.index(".") except Exception as e: - ip="" + ip = "" if len(ip) == 0: - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' This looks not like a valid IP. - ''',title="Unvalid Input") + ''', title="Unvalid Input") sys.exit(0) - # create the letsenscript subscription + # create the letsencrypt subscription try: os.system("clear") subscription = subscriptionsNew(ip, dnsservice, domain, token, target) # success dialog - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' OK your LetsEncrypt subscription is now ready. Go to SUBSCRIBE > LIST to see details. Use the correct port on {0} to reach the service you wanted. - '''.format(domain),title="OK LetsEncrypt Created") + '''.format(domain), title="OK LetsEncrypt Created") except Exception as e: - # unkown error happend - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' -Unkown Error happend - please report to developers: + # unknown error happened + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' +Unknown Error happened - please report to developers: {0} - '''.format(str(e)),title="Exception on Subscription") - sys.exit(1) + '''.format(str(e)), title="Exception on Subscription") + sys.exit(1) -####### COMMANDS ######### + +################## +# COMMANDS +################## ############### # CREATE SSH DIALOG @@ -428,45 +455,44 @@ Unkown Error happend - please report to developers: ############### if sys.argv[1] == "create-ssh-dialog": - - # late imports - so that rest of script can run also if dependency is not available - from dialog import Dialog - menuMakeSubscription() - + sys.exit() -############### +########################## # SUBSCRIPTIONS NEW # call from web interface -############### +########################## if sys.argv[1] == "subscription-new": # check parameters try: - if len(sys.argv) <= 5: raise BlitzError("incorrect parameters","") - ip = sys.argv[2] - dnsservice_type = sys.argv[3] - dnsservice_id = sys.argv[4] - dnsservice_token = sys.argv[5] - if len(sys.argv) <= 6: - target = "ip&tor" - else: - target = sys.argv[6] + if len(sys.argv) <= 5: + raise BlitzError("incorrect parameters", "") except Exception as e: handleException(e) + ip = sys.argv[2] + dnsservice_type = sys.argv[3] + dnsservice_id = sys.argv[4] + dnsservice_token = sys.argv[5] + if len(sys.argv) <= 6: + target = "ip&tor" + else: + target = sys.argv[6] + # create the subscription try: subscription = subscriptionsNew(ip, dnsservice_type, dnsservice_id, dnsservice_token, target) + + # output json ordered bridge + print(json.dumps(subscription, indent=2)) + sys.exit() + except Exception as e: handleException(e) - # output json ordered bridge - print(json.dumps(subscription, indent=2)) - sys.exit() - ####################### # SUBSCRIPTIONS LIST ####################### @@ -483,7 +509,7 @@ if sys.argv[1] == "subscriptions-list": if "subscriptions_letsencrypt" not in subs: subs['subscriptions_letsencrypt'] = [] print(json.dumps(subs['subscriptions_letsencrypt'], indent=2)) - + except Exception as e: handleException(e) @@ -496,11 +522,12 @@ if sys.argv[1] == "subscription-detail": # check parameters try: - if len(sys.argv) <= 2: raise BlitzError("incorrect parameters","") - subscriptionID = sys.argv[2] + if len(sys.argv) <= 2: + raise BlitzError("incorrect parameters", "") except Exception as e: handleException(e) + subscriptionID = sys.argv[2] try: sub = getSubscription(subscriptionID) print(json.dumps(sub, indent=2)) @@ -509,7 +536,7 @@ if sys.argv[1] == "subscription-detail": handleException(e) sys.exit(0) - + ####################### # DOMAIN BY IP # to check if an ip has a domain mapping @@ -518,14 +545,16 @@ if sys.argv[1] == "domain-by-ip": # check parameters try: - if len(sys.argv) <= 2: raise BlitzError("incorrect parameters","") - ip = sys.argv[2] + if len(sys.argv) <= 2: + raise BlitzError("incorrect parameters", "") + except Exception as e: handleException(e) + ip = sys.argv[2] try: - domain=getDomainByIP(ip) + domain = getDomainByIP(ip) print("domain='{0}'".format(domain)) except Exception as e: @@ -540,19 +569,18 @@ if sys.argv[1] == "subscription-cancel": # check parameters try: - if len(sys.argv) <= 2: raise BlitzError("incorrect parameters","") - subscriptionID = sys.argv[2] + if len(sys.argv) <= 2: + raise BlitzError("incorrect parameters", "") except Exception as e: handleException(e) + subscriptionID = sys.argv[2] try: - subscriptionsCancel(subscriptionID) - except Exception as e: handleException(e) sys.exit(0) -# unkown command -print("# unkown command") +# unknown command +print("# unknown command") diff --git a/home.admin/config.scripts/blitz.subscriptions.py b/home.admin/config.scripts/blitz.subscriptions.py index ee47caeab..6aba55e6a 100644 --- a/home.admin/config.scripts/blitz.subscriptions.py +++ b/home.admin/config.scripts/blitz.subscriptions.py @@ -4,16 +4,15 @@ # SSH Dialogs to manage Subscriptions on the RaspiBlitz ######################################################## -import sys -import math -import time -import toml import os import subprocess +import sys +import time +from datetime import datetime -from dialog import Dialog - +import toml from blitzpy import RaspiBlitzConfig +from dialog import Dialog # constants for standard services LND_REST_API = "LND-REST-API" @@ -26,25 +25,32 @@ cfg = RaspiBlitzConfig() cfg.reload() # basic values -SUBSCRIPTIONS_FILE="/mnt/hdd/app-data/subscriptions/subscriptions.toml" +SUBSCRIPTIONS_FILE = "/mnt/hdd/app-data/subscriptions/subscriptions.toml" -####### HELPER FUNCTIONS ######### + +####################### +# HELPER FUNCTIONS +####################### def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) + def parseDateIP2TORSERVER(datestr): - return datetime.datetime.strptime(datestr,"%Y-%m-%dT%H:%M:%S.%fZ") + return datetime.strptime(datestr, "%Y-%m-%dT%H:%M:%S.%fZ") + def secondsLeft(dateObj): - return round((dateObj - datetime.datetime.utcnow()).total_seconds()) + return round((dateObj - datetime.utcnow()).total_seconds()) -####### SSH MENU FUNCTIONS ######### + +####################### +# SSH MENU FUNCTIONS +####################### def mySubscriptions(): - # check if any subscriptions are available - countSubscriptions=0 + countSubscriptions = 0 try: os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) subs = toml.load(SUBSCRIPTIONS_FILE) @@ -52,67 +58,69 @@ def mySubscriptions(): countSubscriptions += len(subs['subscriptions_ip2tor']) if 'subscriptions_letsencrypt' in subs: countSubscriptions += len(subs['subscriptions_letsencrypt']) - except Exception as e: pass + except Exception as e: + pass if countSubscriptions == 0: - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' You have no active or inactive subscriptions. - ''',title="Info") + ''', title="Info") return - + # load subscriptions and make dialog choices out of it choices = [] lookup = {} - lookupIndex=0 - subs = toml.load(SUBSCRIPTIONS_FILE) - + lookupIndex = 0 + subs = toml.load(SUBSCRIPTIONS_FILE) + # list ip2tor subscriptions if 'subscriptions_ip2tor' in subs: for sub in subs['subscriptions_ip2tor']: # remember subscription under lookupindex lookupIndex += 1 - lookup[str(lookupIndex)]=sub + lookup[str(lookupIndex)] = sub # add to dialog choices if sub['active']: - activeState="active" + activeState = "active" else: - activeState="in-active" - name="IP2TOR Bridge for {0}".format(sub['name']) - choices.append( ("{0}".format(lookupIndex), "{0} ({1})".format(name.ljust(30), activeState)) ) + activeState = "in-active" + name = "IP2TOR Bridge for {0}".format(sub['name']) + choices.append(("{0}".format(lookupIndex), "{0} ({1})".format(name.ljust(30), activeState))) # list letsencrypt subscriptions if 'subscriptions_letsencrypt' in subs: for sub in subs['subscriptions_letsencrypt']: # remember subscription under lookupindex lookupIndex += 1 - lookup[str(lookupIndex)]=sub + lookup[str(lookupIndex)] = sub # add to dialog choices if sub['active']: - activeState="active" + activeState = "active" else: - activeState="in-active" - name="LETSENCRYPT {0}".format(sub['id']) - choices.append( ("{0}".format(lookupIndex), "{0} ({1})".format(name.ljust(30), activeState)) ) - + activeState = "in-active" + name = "LETSENCRYPT {0}".format(sub['id']) + choices.append(("{0}".format(lookupIndex), "{0} ({1})".format(name.ljust(30), activeState))) + # show menu with options - d = Dialog(dialog="dialog",autowidgetsize=True) + d = Dialog(dialog="dialog", autowidgetsize=True) d.set_background_title("RaspiBlitz Subscriptions") code, tag = d.menu( "\nYou have the following subscriptions - select for details:", choices=choices, cancel_label="Back", width=65, height=15, title="My Subscriptions") - + # if user chosses CANCEL - if code != d.OK: return + if code != d.OK: + return # get data of selected subscrption selectedSub = lookup[str(tag)] # show details of selected - d = Dialog(dialog="dialog",autowidgetsize=True) + d = Dialog(dialog="dialog", autowidgetsize=True) d.set_background_title("My Subscriptions") if selectedSub['type'] == "letsencrypt-v1": if len(selectedSub['warning']) > 0: selectedSub['warning'] = "\n{0}".format(selectedSub['warning']) - text=''' + text = ''' This is a LetsEncrypt subscription using the free DNS service {dnsservice} @@ -127,18 +135,18 @@ The state of the subscription is: {active} {warning} The following additional information is available: {description} -'''.format( dnsservice=selectedSub['dnsservice_type'], - domain=selectedSub['id'], - ip=selectedSub['ip'], - active= "ACTIVE" if selectedSub['active'] else "NOT ACTIVE", - warning=selectedSub['warning'], - description=selectedSub['description'] - ) +'''.format(dnsservice=selectedSub['dnsservice_type'], + domain=selectedSub['id'], + ip=selectedSub['ip'], + active="ACTIVE" if selectedSub['active'] else "NOT ACTIVE", + warning=selectedSub['warning'], + description=selectedSub['description'] + ) elif selectedSub['type'] == "ip2tor-v1": if len(selectedSub['warning']) > 0: selectedSub['warning'] = "\n{0}".format(selectedSub['warning']) - text=''' + text = ''' This is a IP2TOR subscription bought on {initdate} at {shop} @@ -153,37 +161,43 @@ The state of the subscription is: {active} {warning} The following additional information is available: {description} -'''.format( initdate=selectedSub['time_created'], - shop=selectedSub['shop'], - publicaddress="{0}:{1}".format(selectedSub['ip'],selectedSub['port']), - toraddress=selectedSub['tor'], - renewhours=(round(int(selectedSub['duration'])/3600)), - renewsats=(round(int(selectedSub['price_extension'])/1000)), - totalsats=(round(int(selectedSub['price_extension'])/1000)), - active= "ACTIVE" if selectedSub['active'] else "NOT ACTIVE", - warning=selectedSub['warning'], - description=selectedSub['description'], - service=selectedSub['name'] - ) +'''.format(initdate=selectedSub['time_created'], + shop=selectedSub['shop'], + publicaddress="{0}:{1}".format(selectedSub['ip'], selectedSub['port']), + toraddress=selectedSub['tor'], + renewhours=(round(int(selectedSub['duration']) / 3600)), + renewsats=(round(int(selectedSub['price_extension']) / 1000)), + totalsats=(round(int(selectedSub['price_extension']) / 1000)), + active="ACTIVE" if selectedSub['active'] else "NOT ACTIVE", + warning=selectedSub['warning'], + description=selectedSub['description'], + service=selectedSub['name'] + ) + else: + text = "no text?! FIXME" if selectedSub['active']: extraLable = "CANCEL SUBSCRIPTION" else: extraLable = "DELETE SUBSCRIPTION" - code = d.msgbox(text, title="Subscription Detail", ok_label="Back", extra_button=True, extra_label=extraLable ,width=75, height=30) - + code = d.msgbox(text, title="Subscription Detail", ok_label="Back", extra_button=True, extra_label=extraLable, + width=75, height=30) + # user wants to delete this subscription - # call the responsible sub script for deletion just in case any subscription needs to do some extra api calls when canceling + # call the responsible sub script for deletion just in case any subscription needs to do some extra + # api calls when canceling if code == "extra": os.system("clear") if selectedSub['type'] == "letsencrypt-v1": - cmd="python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py subscription-cancel {0}".format(selectedSub['id']) - print("# running: {0}".format(cmd)) + cmd = "python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py subscription-cancel {0}".format( + selectedSub['id']) + print("# running: {0}".format(cmd)) os.system(cmd) time.sleep(2) elif selectedSub['type'] == "ip2tor-v1": - cmd="python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py subscription-cancel {0}".format(selectedSub['id']) - print("# running: {0}".format(cmd)) + cmd = "python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py subscription-cancel {0}".format( + selectedSub['id']) + print("# running: {0}".format(cmd)) os.system(cmd) time.sleep(2) else: @@ -193,14 +207,17 @@ The following additional information is available: # loop until no more subscriptions or user chooses CANCEL on subscription list mySubscriptions() -####### SSH MENU ######### -choices = [] -choices.append( ("LIST","My Subscriptions") ) -choices.append( ("NEW1","+ IP2TOR Bridge (paid)") ) -choices.append( ("NEW2","+ LetsEncrypt HTTPS Domain (free)") ) +####################### +# SSH MENU +####################### -d = Dialog(dialog="dialog",autowidgetsize=True) +choices = list() +choices.append(("LIST", "My Subscriptions")) +choices.append(("NEW1", "+ IP2TOR Bridge (paid)")) +choices.append(("NEW2", "+ LetsEncrypt HTTPS Domain (free)")) + +d = Dialog(dialog="dialog", autowidgetsize=True) d.set_background_title("RaspiBlitz Subscriptions") code, tag = d.menu( "\nCheck existing subscriptions or create new:", @@ -210,74 +227,86 @@ code, tag = d.menu( if code != d.OK: sys.exit(0) -####### MANAGE SUBSCRIPTIONS ######### +####################### +# MANAGE SUBSCRIPTIONS +####################### if tag == "LIST": mySubscriptions() sys.exit(0) -####### NEW LETSENCRYPT HTTPS DOMAIN ######### +############################### +# NEW LETSENCRYPT HTTPS DOMAIN +############################### if tag == "NEW2": - # run creating a new IP2TOR subscription os.system("clear") - cmd="python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py create-ssh-dialog" + cmd = "python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py create-ssh-dialog" print("# running: {0}".format(cmd)) os.system(cmd) sys.exit(0) -####### NEW IP2TOR BRIDGE ######### +############################### +# NEW IP2TOR BRIDGE +############################### + if tag == "NEW1": # check if Blitz is running behind TOR cfg.reload() if not cfg.run_behind_tor.value: - Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' + Dialog(dialog="dialog", autowidgetsize=True).msgbox(''' The IP2TOR service just makes sense if you run your RaspiBlitz behind TOR. - ''',title="Info") + ''', title="Info") sys.exit(1) os.system("clear") print("please wait ..") # check for which standard services already a active bridge exists - lnd_rest_api=False - lnd_grpc_api=False - lnbits=False - btcpay=False + lnd_rest_api = False + lnd_grpc_api = False + lnbits = False + btcpay = False try: if os.path.isfile(SUBSCRIPTIONS_FILE): os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) subs = toml.load(SUBSCRIPTIONS_FILE) for sub in subs['subscriptions_ip2tor']: - if not sub['active']: next - if sub['active'] and sub['name'] == LND_REST_API: lnd_rest_api=True - if sub['active'] and sub['name'] == LND_GRPC_API: lnd_grpc_api=True - if sub['active'] and sub['name'] == LNBITS: lnbits=True - if sub['active'] and sub['name'] == BTCPAY: btcpay=True + if not sub['active']: + continue + if sub['active'] and sub['name'] == LND_REST_API: + lnd_rest_api = True + if sub['active'] and sub['name'] == LND_GRPC_API: + lnd_grpc_api = True + if sub['active'] and sub['name'] == LNBITS: + lnbits = True + if sub['active'] and sub['name'] == BTCPAY: + btcpay = True except Exception as e: print(e) # check if BTCPayserver is installed - btcPayServer=False - statusData= subprocess.run(['/home/admin/config.scripts/bonus.btcpayserver.sh', 'status'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() + btcPayServer = False + statusData = subprocess.run(['/home/admin/config.scripts/bonus.btcpayserver.sh', 'status'], + stdout=subprocess.PIPE).stdout.decode('utf-8').strip() if statusData.find("installed=1") > -1: - btcPayServer=True + btcPayServer = True # ask user for which RaspiBlitz service the bridge should be used - choices = [] - choices.append( ("REST","LND REST API {0}".format("--> ALREADY BRIDGED" if lnd_rest_api else "")) ) - choices.append( ("GRPC","LND gRPC API {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else "")) ) + choices = list() + choices.append(("REST", "LND REST API {0}".format("--> ALREADY BRIDGED" if lnd_rest_api else ""))) + choices.append(("GRPC", "LND gRPC API {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else ""))) if cfg.lnbits: - choices.append( ("LNBITS","LNbits Webinterface {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else "")) ) + choices.append(("LNBITS", "LNbits Webinterface {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else ""))) if btcPayServer: - choices.append( ("BTCPAY","BTCPay Server Webinterface {0}".format("--> ALREADY BRIDGED" if btcpay else "")) ) - choices.append( ("SELF","Create a custom IP2TOR Bridge") ) + choices.append(("BTCPAY", "BTCPay Server Webinterface {0}".format("--> ALREADY BRIDGED" if btcpay else ""))) + choices.append(("SELF", "Create a custom IP2TOR Bridge")) - d = Dialog(dialog="dialog",autowidgetsize=True) + d = Dialog(dialog="dialog", autowidgetsize=True) d.set_background_title("RaspiBlitz Subscriptions") code, tag = d.menu( "\nChoose RaspiBlitz Service to create Bridge for:", @@ -287,31 +316,35 @@ your RaspiBlitz behind TOR. if code != d.OK: sys.exit(0) - servicename=None - torAddress=None - torPort=None + servicename = None + torAddress = None + torPort = None if tag == "REST": # get TOR address for REST - servicename=LND_REST_API - torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrest8080/hostname'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() - torPort=8080 + servicename = LND_REST_API + torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrest8080/hostname'], + stdout=subprocess.PIPE).stdout.decode('utf-8').strip() + torPort = 8080 if tag == "GRPC": # get TOR address for GRPC - servicename=LND_GRPC_API - torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrpc10009/hostname'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() - torPort=10009 + servicename = LND_GRPC_API + torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrpc10009/hostname'], + stdout=subprocess.PIPE).stdout.decode('utf-8').strip() + torPort = 10009 if tag == "LNBITS": # get TOR address for LNBits - servicename=LNBITS - torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lnbits/hostname'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() - torPort=443 + servicename = LNBITS + torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lnbits/hostname'], + stdout=subprocess.PIPE).stdout.decode('utf-8').strip() + torPort = 443 if tag == "BTCPAY": # get TOR address for BTCPAY - servicename=BTCPAY - torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/btcpay/hostname'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() - torPort=443 + servicename = BTCPAY + torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/btcpay/hostname'], + stdout=subprocess.PIPE).stdout.decode('utf-8').strip() + torPort = 443 if tag == "SELF": - servicename="CUSTOM" + servicename = "CUSTOM" try: # get custom TOR address code, text = d.inputbox( @@ -320,9 +353,11 @@ your RaspiBlitz behind TOR. title="IP2TOR Bridge Target") text = text.strip() os.system("clear") - if code != d.OK: sys.exit(0) - if len(text) == 0: sys.exit(0) - if text.find('.onion') < 0 or text.find(' ') > 0 : + if code != d.OK: + sys.exit(0) + if len(text) == 0: + sys.exit(0) + if text.find('.onion') < 0 or text.find(' ') > 0: print("Not a TOR Onion Address") time.sleep(3) sys.exit(0) @@ -334,8 +369,10 @@ your RaspiBlitz behind TOR. title="IP2TOR Bridge Target") text = text.strip() os.system("clear") - if code != d.OK: sys.exit(0) - if len(text) == 0: sys.exit(0) + if code != d.OK: + sys.exit(0) + if len(text) == 0: + sys.exit(0) torPort = int(text) except Exception as e: print(e) @@ -344,7 +381,8 @@ your RaspiBlitz behind TOR. # run creating a new IP2TOR subscription os.system("clear") - cmd="python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py create-ssh-dialog {0} {1} {2}".format(servicename,torAddress,torPort) + cmd = "python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py create-ssh-dialog {0} {1} {2}".format( + servicename, torAddress, torPort) print("# running: {0}".format(cmd)) os.system(cmd) - sys.exit(0) \ No newline at end of file + sys.exit(0)