From e92346ef44242666e30d153fd59a71cb79fef1ec Mon Sep 17 00:00:00 2001 From: Believethehype Date: Sun, 26 Nov 2023 13:10:19 +0100 Subject: [PATCH] added public zaps for bot --- bot.py | 39 +++++++++++++++++++++++---------------- dvm.py | 26 +++++++++++++------------- playground.py | 4 ++-- utils/zap_utils.py | 39 +++++++++++++++++++++++++++++---------- 4 files changed, 67 insertions(+), 41 deletions(-) diff --git a/bot.py b/bot.py index 2fb3781..9d2c993 100644 --- a/bot.py +++ b/bot.py @@ -11,7 +11,7 @@ from utils.backend_utils import get_amount_per_task from utils.database_utils import get_or_add_user, update_user_balance, create_sql_table, update_sql_table, User from utils.definitions import EventDefinitions from utils.nostr_utils import send_event -from utils.zap_utils import parse_zap_event_tags, pay_bolt11_ln_bits +from utils.zap_utils import parse_zap_event_tags, pay_bolt11_ln_bits, zap class Bot: @@ -219,26 +219,33 @@ class Bot: elif status == "payment-required" or status == "partial": + amount = 0 for tag in nostr_event.tags(): if tag.as_vec()[0] == "amount": amount_msats = int(tag.as_vec()[1]) - amount = str(amount_msats / 1000) + amount = int(amount_msats / 1000) - if len(tag.as_vec()) > 2: - bolt11 = tag.as_vec()[2] - entry = next((x for x in self.job_list if x['event_id'] == etag), None) - if entry is not None and entry['is_paid'] is False and entry['dvm_key'] == ptag: + entry = next((x for x in self.job_list if x['event_id'] == etag), None) + if entry is not None and entry['is_paid'] is False and entry['dvm_key'] == ptag: + + #if we get a bolt11, we pay and move on + if len(tag.as_vec()) > 2: + bolt11 = tag.as_vec()[2] + + # else we create a zap + else: + bolt11 = zap("ai@bitcoinfixesthis.org", amount, "Zap", ptag, etag, self.keys, self.dvm_config) + if bolt11 == None: + print("Receiver has no Lightning address") + return + try: + payment_hash = pay_bolt11_ln_bits(bolt11, self.dvm_config) + self.job_list[self.job_list.index(entry)]['is_paid'] = True + print("[" + self.NAME + "] payment_hash: " + payment_hash + + " Forwarding payment of " + amount + " Sats to DVM") + except Exception as e: + print(e) - try: - payment_hash = pay_bolt11_ln_bits(bolt11, self.dvm_config) - self.job_list[self.job_list.index(entry)]['is_paid'] = True - print("[" + self.NAME + "] payment_hash: " + payment_hash + - " Forwarding payment of " + amount + " Sats to DVM") - except Exception as e: - print(e) - else: - print("not implemented: request bolt11 invoice") - # TODO request zap invoice except Exception as e: print(e) diff --git a/dvm.py b/dvm.py index eae9ab2..4e5a528 100644 --- a/dvm.py +++ b/dvm.py @@ -157,23 +157,23 @@ class DVM: do_work(nip90_event) # if task is directed to us via p tag and user has balance, do the job and update balance - elif p == Keys.from_sk_str(self.dvm_config.PRIVATE_KEY).public_key().to_hex(): - if user.balance > amount: - balance = max(user.balance - amount, 0) - update_sql_table(db=self.dvm_config.DB, npub=user.npub, balance=balance, - iswhitelisted=user.iswhitelisted, isblacklisted=user.isblacklisted, - nip05=user.nip05, lud16=user.lud16, name=user.name, - lastactive=Timestamp.now().as_secs()) + elif p == Keys.from_sk_str(self.dvm_config.PRIVATE_KEY).public_key().to_hex() and user.balance >= amount: - print( - "[" + self.dvm_config.NIP89.name + "] Using user's balance for task: " + task + + balance = max(user.balance - amount, 0) + update_sql_table(db=self.dvm_config.DB, npub=user.npub, balance=balance, + iswhitelisted=user.iswhitelisted, isblacklisted=user.isblacklisted, + nip05=user.nip05, lud16=user.lud16, name=user.name, + lastactive=Timestamp.now().as_secs()) - ". Starting processing.. New balance is: " + str(balance)) + print( + "[" + self.dvm_config.NIP89.name + "] Using user's balance for task: " + task + - send_job_status_reaction(nip90_event, "processing", True, 0, - client=self.client, dvm_config=self.dvm_config) + ". Starting processing.. New balance is: " + str(balance)) - do_work(nip90_event) + send_job_status_reaction(nip90_event, "processing", True, 0, + client=self.client, dvm_config=self.dvm_config) + + do_work(nip90_event) #else send a payment required event to user else: diff --git a/playground.py b/playground.py index 9666a9f..b2fb743 100644 --- a/playground.py +++ b/playground.py @@ -95,8 +95,8 @@ def build_translator(name, dm_allowed_keys): def build_unstable_diffusion(name, dm_allowed_keys): dvm_config = DVMConfig() dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY") - dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY") - dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST") + dvm_config.LNBITS_INVOICE_KEY = "" #This one will not use Lnbits to create invoices, but rely on zaps + dvm_config.LNBITS_URL = "" dvm_config.DM_ALLOWED = dm_allowed_keys # A module might have options it can be initialized with, here we set a default model, and the nova-server diff --git a/utils/zap_utils.py b/utils/zap_utils.py index 880ad89..bd71999 100644 --- a/utils/zap_utils.py +++ b/utils/zap_utils.py @@ -1,5 +1,6 @@ # LIGHTNING FUNCTIONS import json +import urllib.parse import requests from Crypto.Cipher import AES @@ -7,6 +8,7 @@ from bech32 import bech32_decode, convertbits, bech32_encode from nostr_sdk import nostr_sdk, PublicKey, SecretKey, Event, EventBuilder, Tag from utils.dvmconfig import DVMConfig from utils.nostr_utils import get_event_by_id +import lnurl def parse_amount_from_bolt11_invoice(bolt11_invoice: str) -> int: @@ -95,6 +97,7 @@ def check_bolt11_ln_bits_is_paid(payment_hash: str, config: DVMConfig): except Exception as e: return None + def pay_bolt11_ln_bits(bolt11: str, config: DVMConfig): url = config.LNBITS_URL + "/api/v1/payments" data = {'out': True, 'bolt11': bolt11} @@ -147,16 +150,32 @@ def decrypt_private_zap_message(msg: str, privkey: SecretKey, pubkey: PublicKey) return str(ex) -def zap_request(lud16, recipientPubkey, amount_in_sats, keys, dvm_config): - amount_tag = Tag.parse(['amount', str(amount_in_sats*1000)]) - relays_tag = Tag.parse(['relays', dvm_config.RELAY_LIST]) - p_tag = Tag.parse(['p', recipientPubkey]) +def zap(lud16: str, amount: int, zap_type, content, recipient_pubkey, zapped_event, keys, dvm_config): + if lud16.startswith("LNURL") or lud16.startswith("lnurl"): + url = lnurl.decode(lud16) + elif '@' in lud16: #LNaddress + url = 'https://' + str(lud16).split('@')[1] + '/.well-known/lnurlp/' + str(lud16).split('@')[0] + else: # No lud16 set or format invalid + return None + try: + response = requests.get(url) + ob = json.loads(response.content) + callback = ob["callback"] + encoded_lnurl = lnurl.encode(url) + amount_tag = Tag.parse(['amount', str(amount * 1000)]) + relays_tag = Tag.parse(['relays', str(dvm_config.RELAY_LIST)]) + p_tag = Tag.parse(['p', recipient_pubkey]) + e_tag = Tag.parse(['e', zapped_event]) + lnurl_tag = Tag.parse(['lnurl', encoded_lnurl]) + zap_request = EventBuilder(9734, content, + [amount_tag, relays_tag, p_tag, e_tag, lnurl_tag]).to_event(keys).as_json() - #_, encrypted_msg = bech32_encode(parts[0]) + response = requests.get(callback + "?amount=" + str(int(amount) * 1000) + "&nostr=" + urllib.parse.quote_plus( + zap_request) + "&lnurl=" + encoded_lnurl) + ob = json.loads(response.content) + return ob["pr"] - lnurl_tag = Tag.parse(['lnurl', recipientPubkey]) - - - zaprequest = EventBuilder(9734, "", - [amount_tag, relays_tag, p_tag,lnurl_tag ]).to_event(keys) + except Exception as e: + print(e) + return None \ No newline at end of file