mirror of
https://github.com/believethehype/nostrdvm.git
synced 2025-11-19 12:17:40 +01:00
basic bot is working, spawn dvms dependent on config
This commit is contained in:
@@ -24,7 +24,5 @@ TASK_IMAGE_GENERATION_NIP89_DTAG3 = "asdasd"
|
|||||||
|
|
||||||
|
|
||||||
#Backend Specific Options for tasks that require them
|
#Backend Specific Options for tasks that require them
|
||||||
#nova-server is a local backend supporting some AI modules and needs to be installed separately,
|
OPENAI_API_KEY = "" # Enter your OpenAI API Key to use DVMs with OpenAI services
|
||||||
#if dvms supporting it should be used
|
NOVA_SERVER = "" # Enter the address of a nova-server instance, locally or on a machine in your network host:port
|
||||||
OPENAI_API_KEY = "your-openai-api-key"
|
|
||||||
NOVA_SERVER = "127.0.0.1:37318"
|
|
||||||
33
README.md
33
README.md
@@ -1,19 +1,28 @@
|
|||||||
# NostrAI Data Vending Machine
|
# NostrAI: Nostr NIP90 Data Vending Machine Framework
|
||||||
|
|
||||||
This example DVM implementation in Python currently supports simple translations using Google translate, as well as extraction of text from links with pdf files.
|
This framework provides a way to easily build and/or run `Nostr NIP90 DVMs in Python`.
|
||||||
|
|
||||||
At a later stage, additional example tasks will be added, as well as the integration into a larger Machine Learning backend
|
This project is currently under development and additional tasks and features are added along the way.
|
||||||
|
This means the project is in alpha status, interfaces might still change/break.
|
||||||
|
|
||||||
|
|
||||||
Place .env file (based on .env_example) in main folder, install requirements.txt (python 3.10) run main.py. Optionally supports LNbits to create invoices instead of lnaddresses.
|
## To get started:
|
||||||
|
(Tested on Python 3.10)
|
||||||
|
|
||||||
Use vendata.io to create a nip89 announcement of your dvm and save the dtag in your .env config.
|
Create a new venv by running `"python -m venv venv"`
|
||||||
|
- Place .env file (based on .env_example) in main folder.
|
||||||
|
- Set your own private hex keys, create NIP89 dtags on vendata.io,
|
||||||
|
- Install requirements.txt
|
||||||
|
- Run python main.py.
|
||||||
|
|
||||||
A tutorial on how to add additional tasks, as well as the larger server backend will be added soon.
|
In `playground.py` some DVMs are already prepared. Feel free to play along with the existing ones.
|
||||||
|
You can also add new tasks by using the interface, just like the existing tasks in the `tasks` folder.
|
||||||
|
|
||||||
Known Issues:
|
A `bot` is running by default that lists and communicates with the `DVMs` added to it,
|
||||||
- After refactoring DVMs work independent from each other for the most part.
|
so your DVMs can be controled via any regular client as well.
|
||||||
- Some functions might work easier than they did before (need some refactoring)
|
|
||||||
- Bot currently not implemented
|
The Framework optionally supports `LNbits` to create invoices instead of using a `lightning address`. If LNBits is not used,
|
||||||
- Some basic functionality is still missing, e.g. handling various mediasources
|
make sure your nostr accounts have a valid lightning address.
|
||||||
- Interface might still change a lot and brick things.
|
|
||||||
|
|
||||||
|
A tutorial on how to add additional tasks, as well as the larger server backend will be added at a later stage.
|
||||||
|
|||||||
95
bot.py
95
bot.py
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
from nostr_sdk import Keys, Client, Timestamp, Filter, nip04_decrypt, HandleNotification, EventBuilder, PublicKey, \
|
from nostr_sdk import Keys, Client, Timestamp, Filter, nip04_decrypt, HandleNotification, EventBuilder, PublicKey, \
|
||||||
Event, Options
|
Event, Options
|
||||||
@@ -10,7 +11,8 @@ 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.database_utils import get_or_add_user, update_user_balance, create_sql_table, update_sql_table, User
|
||||||
from utils.definitions import EventDefinitions
|
from utils.definitions import EventDefinitions
|
||||||
from utils.nostr_utils import send_event, get_event_by_id
|
from utils.nostr_utils import send_event, get_event_by_id
|
||||||
from utils.zap_utils import parse_amount_from_bolt11_invoice, check_for_zapplepay, decrypt_private_zap_message
|
from utils.zap_utils import parse_amount_from_bolt11_invoice, check_for_zapplepay, decrypt_private_zap_message, \
|
||||||
|
parse_zap_event_tags
|
||||||
|
|
||||||
|
|
||||||
class Bot:
|
class Bot:
|
||||||
@@ -28,7 +30,8 @@ class Bot:
|
|||||||
|
|
||||||
pk = self.keys.public_key()
|
pk = self.keys.public_key()
|
||||||
|
|
||||||
print("Nostr BOT public key: " + str(pk.to_bech32()) + " Hex: " + str(pk.to_hex()) + " Name: " + self.NAME + " Supported DVM tasks: " +
|
print("Nostr BOT public key: " + str(pk.to_bech32()) + " Hex: " + str(pk.to_hex()) + " Name: " + self.NAME +
|
||||||
|
" Supported DVM tasks: " +
|
||||||
', '.join(p.NAME + ":" + p.TASK for p in self.dvm_config.SUPPORTED_DVMS) + "\n")
|
', '.join(p.NAME + ":" + p.TASK for p in self.dvm_config.SUPPORTED_DVMS) + "\n")
|
||||||
|
|
||||||
for relay in self.dvm_config.RELAY_LIST:
|
for relay in self.dvm_config.RELAY_LIST:
|
||||||
@@ -36,8 +39,7 @@ class Bot:
|
|||||||
self.client.connect()
|
self.client.connect()
|
||||||
|
|
||||||
zap_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_ZAP]).since(Timestamp.now())
|
zap_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_ZAP]).since(Timestamp.now())
|
||||||
dm_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM]).since(
|
dm_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM]).since(Timestamp.now())
|
||||||
Timestamp.now())
|
|
||||||
|
|
||||||
self.client.subscribe([zap_filter, dm_filter])
|
self.client.subscribe([zap_filter, dm_filter])
|
||||||
|
|
||||||
@@ -50,7 +52,7 @@ class Bot:
|
|||||||
keys = self.keys
|
keys = self.keys
|
||||||
|
|
||||||
def handle(self, relay_url, nostr_event):
|
def handle(self, relay_url, nostr_event):
|
||||||
if EventDefinitions.KIND_DM:
|
if nostr_event.kind() == EventDefinitions.KIND_DM:
|
||||||
handle_dm(nostr_event)
|
handle_dm(nostr_event)
|
||||||
elif nostr_event.kind() == EventDefinitions.KIND_ZAP:
|
elif nostr_event.kind() == EventDefinitions.KIND_ZAP:
|
||||||
handle_zap(nostr_event)
|
handle_zap(nostr_event)
|
||||||
@@ -63,24 +65,20 @@ class Bot:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
decrypted_text = nip04_decrypt(self.keys.secret_key(), nostr_event.pubkey(), nostr_event.content())
|
decrypted_text = nip04_decrypt(self.keys.secret_key(), nostr_event.pubkey(), nostr_event.content())
|
||||||
user = get_or_add_user(db=self.dvm_config.DB, npub=sender, client=self.client)
|
user = get_or_add_user(db=self.dvm_config.DB, npub=sender, client=self.client, config=self.dvm_config)
|
||||||
|
|
||||||
# user = User
|
|
||||||
# user.npub = sender
|
|
||||||
# user.balance = 250
|
|
||||||
# user.iswhitelisted = False
|
|
||||||
# user.isblacklisted = False
|
|
||||||
# user.name = "Test"
|
|
||||||
# user.nip05 = "Test@test"
|
|
||||||
# user.lud16 = "Test@test"
|
|
||||||
|
|
||||||
# We do a selection of tasks now, maybe change this later, Idk.
|
# We do a selection of tasks now, maybe change this later, Idk.
|
||||||
if decrypted_text[0].isdigit():
|
if decrypted_text[0].isdigit():
|
||||||
index = int(decrypted_text.split(' ')[0]) - 1
|
index = int(decrypted_text.split(' ')[0]) - 1
|
||||||
task = self.dvm_config.SUPPORTED_DVMS[index].TASK
|
task = self.dvm_config.SUPPORTED_DVMS[index].TASK
|
||||||
print("["+ self.NAME + "] Request from " + str(user.name) + " (" + str(user.nip05) + ", Balance: "+ str(user.balance)+ " Sats) Task: " + str(task))
|
print("[" + self.NAME + "] Request from " + str(user.name) + " (" + str(user.nip05) + ", Balance: "
|
||||||
|
+ str(user.balance)+ " Sats) Task: " + str(task))
|
||||||
|
|
||||||
|
duration = 1
|
||||||
|
required_amount = get_amount_per_task(self.dvm_config.SUPPORTED_DVMS[index].TASK,
|
||||||
|
self.dvm_config, duration)
|
||||||
|
|
||||||
required_amount = self.dvm_config.SUPPORTED_DVMS[index].COST
|
|
||||||
|
|
||||||
if user.isblacklisted:
|
if user.isblacklisted:
|
||||||
# For some reason an admin might blacklist npubs, e.g. for abusing the service
|
# For some reason an admin might blacklist npubs, e.g. for abusing the service
|
||||||
@@ -135,8 +133,8 @@ class Bot:
|
|||||||
print("payment-required")
|
print("payment-required")
|
||||||
time.sleep(2.0)
|
time.sleep(2.0)
|
||||||
evt = EventBuilder.new_encrypted_direct_msg(self.keys, nostr_event.pubkey(),
|
evt = EventBuilder.new_encrypted_direct_msg(self.keys, nostr_event.pubkey(),
|
||||||
"Balance required, please zap me with at least " + str(
|
"Balance required, please zap me with at least " +
|
||||||
int(required_amount - user.balance))
|
str(int(required_amount - user.balance))
|
||||||
+ " Sats, then try again.",
|
+ " Sats, then try again.",
|
||||||
nostr_event.id()).to_event(self.keys)
|
nostr_event.id()).to_event(self.keys)
|
||||||
time.sleep(2.0)
|
time.sleep(2.0)
|
||||||
@@ -149,7 +147,8 @@ class Bot:
|
|||||||
|
|
||||||
dvm_result = json.loads(decrypted_text)
|
dvm_result = json.loads(decrypted_text)
|
||||||
user_npub_hex = dvm_result["sender"]
|
user_npub_hex = dvm_result["sender"]
|
||||||
user = get_or_add_user(db=self.dvm_config.DB, npub=user_npub_hex, client=self.client)
|
user = get_or_add_user(db=self.dvm_config.DB, npub=user_npub_hex,
|
||||||
|
client=self.client, config=self.dvm_config)
|
||||||
print("[" + self.NAME + "] Received results, message to orignal sender " + user.name)
|
print("[" + self.NAME + "] Received results, message to orignal sender " + user.name)
|
||||||
reply_event = EventBuilder.new_encrypted_direct_msg(self.keys,
|
reply_event = EventBuilder.new_encrypted_direct_msg(self.keys,
|
||||||
PublicKey.from_hex(user.npub),
|
PublicKey.from_hex(user.npub),
|
||||||
@@ -159,7 +158,7 @@ class Bot:
|
|||||||
send_event(reply_event, client=self.client, dvm_config=dvm_config)
|
send_event(reply_event, client=self.client, dvm_config=dvm_config)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("Message from " + user.name + ": " + decrypted_text)
|
print("[" + self.NAME + "] Message from " + user.name + ": " + decrypted_text)
|
||||||
message = "DVMs that I support:\n\n"
|
message = "DVMs that I support:\n\n"
|
||||||
index = 1
|
index = 1
|
||||||
for p in self.dvm_config.SUPPORTED_DVMS:
|
for p in self.dvm_config.SUPPORTED_DVMS:
|
||||||
@@ -173,61 +172,45 @@ class Bot:
|
|||||||
#nostr_event.id()).to_event(self.keys)
|
#nostr_event.id()).to_event(self.keys)
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
send_event(evt, client=self.client, dvm_config=dvm_config)
|
send_event(evt, client=self.client, dvm_config=dvm_config)
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
# TODO we still receive (broken content) events after fetching the metadata, but we don't listen to them.
|
||||||
|
# probably in client.get_events_of in fetch_user_metadata
|
||||||
|
print("Error in bot " + str(e))
|
||||||
def handle_zap(zap_event):
|
def handle_zap(zap_event):
|
||||||
zapped_event = None
|
print("[" + self.NAME + "] Zap received")
|
||||||
invoice_amount = 0
|
|
||||||
anon = False
|
|
||||||
sender = zap_event.pubkey()
|
|
||||||
print("Zap received")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for tag in zap_event.tags():
|
invoice_amount, zapped_event, sender, anon = parse_zap_event_tags(zap_event,
|
||||||
if tag.as_vec()[0] == 'bolt11':
|
self.keys, self.NAME,
|
||||||
invoice_amount = parse_amount_from_bolt11_invoice(tag.as_vec()[1])
|
self.client, self.dvm_config)
|
||||||
elif tag.as_vec()[0] == 'e':
|
|
||||||
zapped_event = get_event_by_id(tag.as_vec()[1], client=self.client, config=self.dvm_config)
|
user = get_or_add_user(self.dvm_config.DB, sender, client=self.client, config=self.dvm_config)
|
||||||
elif tag.as_vec()[0] == 'description':
|
|
||||||
zap_request_event = Event.from_json(tag.as_vec()[1])
|
|
||||||
sender = check_for_zapplepay(zap_request_event.pubkey().to_hex(),
|
|
||||||
zap_request_event.content())
|
|
||||||
for z_tag in zap_request_event.tags():
|
|
||||||
if z_tag.as_vec()[0] == 'anon':
|
|
||||||
if len(z_tag.as_vec()) > 1:
|
|
||||||
print("Private Zap received.")
|
|
||||||
decrypted_content = decrypt_private_zap_message(z_tag.as_vec()[1],
|
|
||||||
self.keys.secret_key(),
|
|
||||||
zap_request_event.pubkey())
|
|
||||||
decrypted_private_event = Event.from_json(decrypted_content)
|
|
||||||
if decrypted_private_event.kind() == 9733:
|
|
||||||
sender = decrypted_private_event.pubkey().to_hex()
|
|
||||||
message = decrypted_private_event.content()
|
|
||||||
if message != "":
|
|
||||||
print("Zap Message: " + message)
|
|
||||||
else:
|
|
||||||
anon = True
|
|
||||||
print("Anonymous Zap received. Unlucky, I don't know from whom, and never will")
|
|
||||||
user = get_or_add_user(self.dvm_config.DB, sender, client=self.client)
|
|
||||||
|
|
||||||
if zapped_event is not None:
|
if zapped_event is not None:
|
||||||
if not anon:
|
if not anon:
|
||||||
print("Note Zap received for Bot balance: " + str(invoice_amount) + " Sats from " + str(
|
print("[" + self.NAME + "] Note Zap received for Bot balance: " + str(invoice_amount) + " Sats from " + str(
|
||||||
user.name))
|
user.name))
|
||||||
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
|
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
|
||||||
config=self.dvm_config)
|
config=self.dvm_config)
|
||||||
|
|
||||||
# a regular note
|
# a regular note
|
||||||
elif not anon:
|
elif not anon:
|
||||||
print("Profile Zap received for Bot balance: " + str(invoice_amount) + " Sats from " + str(
|
print("[" + self.NAME + "] Profile Zap received for Bot balance: " + str(invoice_amount) + " Sats from " + str(
|
||||||
user.name))
|
user.name))
|
||||||
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
|
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
|
||||||
config=self.dvm_config)
|
config=self.dvm_config)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error during content decryption: {e}")
|
print("[" + self.NAME + "] Error during content decryption:" + str(e))
|
||||||
|
|
||||||
self.client.handle_notifications(NotificationHandler())
|
self.client.handle_notifications(NotificationHandler())
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1.0)
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
bot = Bot
|
||||||
|
nostr_dvm_thread = Thread(target=bot, args=[self.dvm_config])
|
||||||
|
nostr_dvm_thread.start()
|
||||||
|
|
||||||
|
|||||||
85
dvm.py
85
dvm.py
@@ -15,15 +15,13 @@ from utils.database_utils import update_sql_table, get_from_sql_table, \
|
|||||||
from utils.nostr_utils import get_event_by_id, get_referenced_event_by_id, send_event
|
from utils.nostr_utils import get_event_by_id, get_referenced_event_by_id, send_event
|
||||||
from utils.output_utils import post_process_result, build_status_reaction
|
from utils.output_utils import post_process_result, build_status_reaction
|
||||||
from utils.zap_utils import check_bolt11_ln_bits_is_paid, parse_amount_from_bolt11_invoice, \
|
from utils.zap_utils import check_bolt11_ln_bits_is_paid, parse_amount_from_bolt11_invoice, \
|
||||||
check_for_zapplepay, decrypt_private_zap_message, create_bolt11_ln_bits
|
check_for_zapplepay, decrypt_private_zap_message, create_bolt11_ln_bits, parse_zap_event_tags
|
||||||
|
|
||||||
use_logger = False
|
use_logger = False
|
||||||
if use_logger:
|
if use_logger:
|
||||||
init_logger(LogLevel.DEBUG)
|
init_logger(LogLevel.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DVM:
|
class DVM:
|
||||||
dvm_config: DVMConfig
|
dvm_config: DVMConfig
|
||||||
admin_config: AdminConfig
|
admin_config: AdminConfig
|
||||||
@@ -32,7 +30,7 @@ class DVM:
|
|||||||
job_list: list
|
job_list: list
|
||||||
jobs_on_hold_list: list
|
jobs_on_hold_list: list
|
||||||
|
|
||||||
def __init__(self, dvmconfig, adminconfig = None):
|
def __init__(self, dvmconfig, adminconfig=None):
|
||||||
self.dvm_config = dvmconfig
|
self.dvm_config = dvmconfig
|
||||||
self.admin_config = adminconfig
|
self.admin_config = adminconfig
|
||||||
self.keys = Keys.from_sk_str(dvmconfig.PRIVATE_KEY)
|
self.keys = Keys.from_sk_str(dvmconfig.PRIVATE_KEY)
|
||||||
@@ -56,7 +54,8 @@ class DVM:
|
|||||||
self.client.connect()
|
self.client.connect()
|
||||||
|
|
||||||
zap_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_ZAP]).since(Timestamp.now())
|
zap_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_ZAP]).since(Timestamp.now())
|
||||||
bot_dm_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM]).authors(self.dvm_config.DM_ALLOWED).since(Timestamp.now())
|
bot_dm_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM]).authors(self.dvm_config.DM_ALLOWED).since(
|
||||||
|
Timestamp.now())
|
||||||
|
|
||||||
kinds = [EventDefinitions.KIND_NIP90_GENERIC]
|
kinds = [EventDefinitions.KIND_NIP90_GENERIC]
|
||||||
for dvm in self.dvm_config.SUPPORTED_DVMS:
|
for dvm in self.dvm_config.SUPPORTED_DVMS:
|
||||||
@@ -75,7 +74,8 @@ class DVM:
|
|||||||
|
|
||||||
def handle(self, relay_url, nostr_event):
|
def handle(self, relay_url, nostr_event):
|
||||||
if EventDefinitions.KIND_NIP90_EXTRACT_TEXT <= nostr_event.kind() <= EventDefinitions.KIND_NIP90_GENERIC:
|
if EventDefinitions.KIND_NIP90_EXTRACT_TEXT <= nostr_event.kind() <= EventDefinitions.KIND_NIP90_GENERIC:
|
||||||
print("[" + self.dvm_config.NIP89.name + "] " + f"Received new NIP90 Job Request from {relay_url}: {nostr_event.as_json()}")
|
print(
|
||||||
|
"[" + self.dvm_config.NIP89.name + "] " + f"Received new NIP90 Job Request from {relay_url}: {nostr_event.as_json()}")
|
||||||
handle_nip90_job_event(nostr_event)
|
handle_nip90_job_event(nostr_event)
|
||||||
elif nostr_event.kind() == EventDefinitions.KIND_ZAP:
|
elif nostr_event.kind() == EventDefinitions.KIND_ZAP:
|
||||||
handle_zap(nostr_event)
|
handle_zap(nostr_event)
|
||||||
@@ -86,7 +86,8 @@ class DVM:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def handle_nip90_job_event(nip90_event):
|
def handle_nip90_job_event(nip90_event):
|
||||||
user = get_or_add_user(self.dvm_config.DB, nip90_event.pubkey().to_hex(), client=self.client)
|
user = get_or_add_user(self.dvm_config.DB, nip90_event.pubkey().to_hex(), client=self.client,
|
||||||
|
config=self.dvm_config)
|
||||||
task_supported, task, duration = check_task_is_supported(nip90_event, client=self.client,
|
task_supported, task, duration = check_task_is_supported(nip90_event, client=self.client,
|
||||||
get_duration=(not user.iswhitelisted),
|
get_duration=(not user.iswhitelisted),
|
||||||
config=self.dvm_config)
|
config=self.dvm_config)
|
||||||
@@ -107,7 +108,8 @@ class DVM:
|
|||||||
task_is_free = True
|
task_is_free = True
|
||||||
|
|
||||||
if user.iswhitelisted or task_is_free:
|
if user.iswhitelisted or task_is_free:
|
||||||
print("[" + self.dvm_config.NIP89.name + "] Free task or Whitelisted for task " + task + ". Starting processing..")
|
print(
|
||||||
|
"[" + self.dvm_config.NIP89.name + "] Free task or Whitelisted for task " + task + ". Starting processing..")
|
||||||
send_job_status_reaction(nip90_event, "processing", True, 0, client=self.client,
|
send_job_status_reaction(nip90_event, "processing", True, 0, client=self.client,
|
||||||
dvm_config=self.dvm_config)
|
dvm_config=self.dvm_config)
|
||||||
do_work(nip90_event, is_from_bot=False)
|
do_work(nip90_event, is_from_bot=False)
|
||||||
@@ -118,7 +120,8 @@ class DVM:
|
|||||||
if tag.as_vec()[0] == 'bid':
|
if tag.as_vec()[0] == 'bid':
|
||||||
bid = int(tag.as_vec()[1])
|
bid = int(tag.as_vec()[1])
|
||||||
|
|
||||||
print("[" + self.dvm_config.NIP89.name + "] Payment required: New Nostr " + task + " Job event: " + nip90_event.as_json())
|
print(
|
||||||
|
"[" + self.dvm_config.NIP89.name + "] Payment required: New Nostr " + task + " Job event: " + nip90_event.as_json())
|
||||||
if bid > 0:
|
if bid > 0:
|
||||||
bid_offer = int(bid / 1000)
|
bid_offer = int(bid / 1000)
|
||||||
if bid_offer >= amount:
|
if bid_offer >= amount:
|
||||||
@@ -127,47 +130,21 @@ class DVM:
|
|||||||
client=self.client, dvm_config=self.dvm_config)
|
client=self.client, dvm_config=self.dvm_config)
|
||||||
|
|
||||||
else: # If there is no bid, just request server rate from user
|
else: # If there is no bid, just request server rate from user
|
||||||
print("[" + self.dvm_config.NIP89.name + "] Requesting payment for Event: " + nip90_event.id().to_hex())
|
print(
|
||||||
|
"[" + self.dvm_config.NIP89.name + "] Requesting payment for Event: " + nip90_event.id().to_hex())
|
||||||
send_job_status_reaction(nip90_event, "payment-required",
|
send_job_status_reaction(nip90_event, "payment-required",
|
||||||
False, amount, client=self.client, dvm_config=self.dvm_config)
|
False, amount, client=self.client, dvm_config=self.dvm_config)
|
||||||
else:
|
else:
|
||||||
print("Task not supported on this DVM, skipping..")
|
print("Task not supported on this DVM, skipping..")
|
||||||
|
|
||||||
def handle_zap(zap_event):
|
def handle_zap(zap_event):
|
||||||
zapped_event = None
|
|
||||||
invoice_amount = 0
|
|
||||||
anon = False
|
|
||||||
sender = zap_event.pubkey()
|
|
||||||
print("Zap received")
|
print("Zap received")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for tag in zap_event.tags():
|
invoice_amount, zapped_event, sender, anon = parse_zap_event_tags(zap_event,
|
||||||
if tag.as_vec()[0] == 'bolt11':
|
self.keys, self.dvm_config.NIP89.name,
|
||||||
invoice_amount = parse_amount_from_bolt11_invoice(tag.as_vec()[1])
|
self.client, self.dvm_config)
|
||||||
elif tag.as_vec()[0] == 'e':
|
user = get_or_add_user(db=self.dvm_config.DB, npub=sender, client=self.client, config=self.dvm_config)
|
||||||
zapped_event = get_event_by_id(tag.as_vec()[1], client=self.client, config=self.dvm_config)
|
|
||||||
elif tag.as_vec()[0] == 'description':
|
|
||||||
zap_request_event = Event.from_json(tag.as_vec()[1])
|
|
||||||
sender = check_for_zapplepay(zap_request_event.pubkey().to_hex(),
|
|
||||||
zap_request_event.content())
|
|
||||||
for ztag in zap_request_event.tags():
|
|
||||||
if ztag.as_vec()[0] == 'anon':
|
|
||||||
if len(ztag.as_vec()) > 1:
|
|
||||||
print("Private Zap received.")
|
|
||||||
decrypted_content = decrypt_private_zap_message(ztag.as_vec()[1],
|
|
||||||
self.keys.secret_key(),
|
|
||||||
zap_request_event.pubkey())
|
|
||||||
decrypted_private_event = Event.from_json(decrypted_content)
|
|
||||||
if decrypted_private_event.kind() == 9733:
|
|
||||||
sender = decrypted_private_event.pubkey().to_hex()
|
|
||||||
message = decrypted_private_event.content()
|
|
||||||
if message != "":
|
|
||||||
print("Zap Message: " + message)
|
|
||||||
else:
|
|
||||||
anon = True
|
|
||||||
print("Anonymous Zap received. Unlucky, I don't know from whom, and never will")
|
|
||||||
user = get_or_add_user(self.dvm_config.DB, sender, client=self.client)
|
|
||||||
|
|
||||||
|
|
||||||
if zapped_event is not None:
|
if zapped_event is not None:
|
||||||
if zapped_event.kind() == EventDefinitions.KIND_FEEDBACK: # if a reaction by us got zapped
|
if zapped_event.kind() == EventDefinitions.KIND_FEEDBACK: # if a reaction by us got zapped
|
||||||
@@ -219,13 +196,15 @@ class DVM:
|
|||||||
elif not anon:
|
elif not anon:
|
||||||
print("Note Zap received for Bot balance: " + str(invoice_amount) + " Sats from " + str(
|
print("Note Zap received for Bot balance: " + str(invoice_amount) + " Sats from " + str(
|
||||||
user.name))
|
user.name))
|
||||||
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client, config=self.dvm_config)
|
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
|
||||||
|
config=self.dvm_config)
|
||||||
|
|
||||||
# a regular note
|
# a regular note
|
||||||
elif not anon:
|
elif not anon:
|
||||||
print("Profile Zap received for Bot balance: " + str(invoice_amount) + " Sats from " + str(
|
print("Profile Zap received for Bot balance: " + str(invoice_amount) + " Sats from " + str(
|
||||||
user.name))
|
user.name))
|
||||||
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client, config=self.dvm_config)
|
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
|
||||||
|
config=self.dvm_config)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error during content decryption: {e}")
|
print(f"Error during content decryption: {e}")
|
||||||
@@ -264,7 +243,8 @@ class DVM:
|
|||||||
input = tag.as_vec()[1]
|
input = tag.as_vec()[1]
|
||||||
input_type = tag.as_vec()[2]
|
input_type = tag.as_vec()[2]
|
||||||
if input_type == "job":
|
if input_type == "job":
|
||||||
evt = get_referenced_event_by_id(event_id=input, client=client, kinds=EventDefinitions.ANY_RESULT,
|
evt = get_referenced_event_by_id(event_id=input, client=client,
|
||||||
|
kinds=EventDefinitions.ANY_RESULT,
|
||||||
dvm_config=dvmconfig)
|
dvm_config=dvmconfig)
|
||||||
if evt is None:
|
if evt is None:
|
||||||
if append:
|
if append:
|
||||||
@@ -287,7 +267,7 @@ class DVM:
|
|||||||
x.result = data
|
x.result = data
|
||||||
x.is_processed = True
|
x.is_processed = True
|
||||||
if self.dvm_config.SHOW_RESULT_BEFORE_PAYMENT and not is_paid:
|
if self.dvm_config.SHOW_RESULT_BEFORE_PAYMENT and not is_paid:
|
||||||
send_nostr_reply_event(data, original_event_str,)
|
send_nostr_reply_event(data, original_event_str, )
|
||||||
send_job_status_reaction(original_event, "success", amount,
|
send_job_status_reaction(original_event, "success", amount,
|
||||||
dvm_config=self.dvm_config) # or payment-required, or both?
|
dvm_config=self.dvm_config) # or payment-required, or both?
|
||||||
elif not self.dvm_config.SHOW_RESULT_BEFORE_PAYMENT and not is_paid:
|
elif not self.dvm_config.SHOW_RESULT_BEFORE_PAYMENT and not is_paid:
|
||||||
@@ -318,10 +298,11 @@ class DVM:
|
|||||||
}
|
}
|
||||||
message = json.dumps(params)
|
message = json.dumps(params)
|
||||||
print(message)
|
print(message)
|
||||||
response_event = EventBuilder.new_encrypted_direct_msg(self.keys, receiver_key, message, None).to_event(self.keys)
|
response_event = EventBuilder.new_encrypted_direct_msg(self.keys, receiver_key, message,
|
||||||
|
None).to_event(self.keys)
|
||||||
send_event(response_event, client=self.client, dvm_config=self.dvm_config)
|
send_event(response_event, client=self.client, dvm_config=self.dvm_config)
|
||||||
else:
|
else:
|
||||||
#Regular DVM reply
|
# Regular DVM reply
|
||||||
send_nostr_reply_event(post_processed_content, original_event_str)
|
send_nostr_reply_event(post_processed_content, original_event_str)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
respond_to_error(str(e), original_event_str, False)
|
respond_to_error(str(e), original_event_str, False)
|
||||||
@@ -347,7 +328,8 @@ class DVM:
|
|||||||
response_kind = original_event.kind() + 1000
|
response_kind = original_event.kind() + 1000
|
||||||
reply_event = EventBuilder(response_kind, str(content), reply_tags).to_event(key)
|
reply_event = EventBuilder(response_kind, str(content), reply_tags).to_event(key)
|
||||||
send_event(reply_event, client=self.client, dvm_config=self.dvm_config)
|
send_event(reply_event, client=self.client, dvm_config=self.dvm_config)
|
||||||
print("[" + self.dvm_config.NIP89.name + "] " + str(response_kind) + " Job Response event sent: " + reply_event.as_json())
|
print("[" + self.dvm_config.NIP89.name + "] " + str(
|
||||||
|
response_kind) + " Job Response event sent: " + reply_event.as_json())
|
||||||
return reply_event.as_json()
|
return reply_event.as_json()
|
||||||
|
|
||||||
def respond_to_error(content: str, original_event_as_str: str, is_from_bot=False):
|
def respond_to_error(content: str, original_event_as_str: str, is_from_bot=False):
|
||||||
@@ -366,7 +348,7 @@ class DVM:
|
|||||||
elif tag.as_vec()[0] == "i":
|
elif tag.as_vec()[0] == "i":
|
||||||
task = tag.as_vec()[1]
|
task = tag.as_vec()[1]
|
||||||
|
|
||||||
user = get_or_add_user(self.dvm_config.DB, sender, self.client)
|
user = get_or_add_user(db=self.dvm_config.DB, npub=sender, client=self.client, config=self.dvm_config)
|
||||||
if not user.iswhitelisted:
|
if not user.iswhitelisted:
|
||||||
amount = int(user.balance) + get_amount_per_task(task, self.dvm_config)
|
amount = int(user.balance) + get_amount_per_task(task, self.dvm_config)
|
||||||
update_sql_table(self.dvm_config.DB, sender, amount, user.iswhitelisted, user.isblacklisted,
|
update_sql_table(self.dvm_config.DB, sender, amount, user.iswhitelisted, user.isblacklisted,
|
||||||
@@ -419,7 +401,7 @@ class DVM:
|
|||||||
status=status, result="", is_processed=False, bolt11=bolt11,
|
status=status, result="", is_processed=False, bolt11=bolt11,
|
||||||
payment_hash=payment_hash,
|
payment_hash=payment_hash,
|
||||||
expires=expires, from_bot=False))
|
expires=expires, from_bot=False))
|
||||||
#print(str(self.job_list))
|
# print(str(self.job_list))
|
||||||
if (status == "payment-required" or status == "payment-rejected" or (
|
if (status == "payment-required" or status == "payment-rejected" or (
|
||||||
status == "processing" and not is_paid)
|
status == "processing" and not is_paid)
|
||||||
or (status == "success" and not is_paid)):
|
or (status == "success" and not is_paid)):
|
||||||
@@ -435,7 +417,7 @@ class DVM:
|
|||||||
|
|
||||||
send_event(event, client=self.client, dvm_config=self.dvm_config)
|
send_event(event, client=self.client, dvm_config=self.dvm_config)
|
||||||
print("[" + self.dvm_config.NIP89.name + "]" + ": Sent Kind " + str(
|
print("[" + self.dvm_config.NIP89.name + "]" + ": Sent Kind " + str(
|
||||||
EventDefinitions.KIND_FEEDBACK) + " Reaction: " + status + " " + event.as_json())
|
EventDefinitions.KIND_FEEDBACK) + " Reaction: " + status + " " + event.as_json())
|
||||||
return event.as_json()
|
return event.as_json()
|
||||||
|
|
||||||
def do_work(job_event, is_from_bot=False):
|
def do_work(job_event, is_from_bot=False):
|
||||||
@@ -495,4 +477,3 @@ class DVM:
|
|||||||
self.jobs_on_hold_list.remove(job)
|
self.jobs_on_hold_list.remove(job)
|
||||||
|
|
||||||
time.sleep(1.0)
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,19 @@ class DVMTaskInterface:
|
|||||||
nip89.content = nip89config.CONTENT
|
nip89.content = nip89config.CONTENT
|
||||||
return nip89
|
return nip89
|
||||||
|
|
||||||
|
def init(self, name, dvm_config, admin_config, nip89config):
|
||||||
|
self.NAME = name
|
||||||
|
self.PK = dvm_config.PRIVATE_KEY
|
||||||
|
if dvm_config.COST is not None:
|
||||||
|
self.COST = dvm_config.COST
|
||||||
|
|
||||||
|
dvm_config.SUPPORTED_DVMS = [self]
|
||||||
|
dvm_config.DB = "db/" + self.NAME + ".db"
|
||||||
|
dvm_config.NIP89 = self.NIP89_announcement(nip89config)
|
||||||
|
self.dvm_config = dvm_config
|
||||||
|
self.admin_config = admin_config
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
nostr_dvm_thread = Thread(target=self.DVM, args=[self.dvm_config, self.admin_config])
|
nostr_dvm_thread = Thread(target=self.DVM, args=[self.dvm_config, self.admin_config])
|
||||||
nostr_dvm_thread.start()
|
nostr_dvm_thread.start()
|
||||||
|
|||||||
64
main.py
64
main.py
@@ -17,41 +17,49 @@ def run_nostr_dvm_with_local_config():
|
|||||||
# We extract the Publickey from our bot, so the DVMs know who they should listen and react to.
|
# We extract the Publickey from our bot, so the DVMs know who they should listen and react to.
|
||||||
bot_publickey = Keys.from_sk_str(os.getenv("BOT_PRIVATE_KEY")).public_key()
|
bot_publickey = Keys.from_sk_str(os.getenv("BOT_PRIVATE_KEY")).public_key()
|
||||||
|
|
||||||
# Spawn some DVMs in the playground and run them
|
|
||||||
# You can add arbitrary DVMs there and instantiate them here
|
|
||||||
|
|
||||||
# Spawn DVM1 Kind 5000 Text Extractor from PDFs
|
|
||||||
pdfextractor = build_pdf_extractor("PDF Extractor", [bot_publickey])
|
|
||||||
pdfextractor.run()
|
|
||||||
|
|
||||||
# Spawn DVM2 Kind 5002 Text Translation
|
|
||||||
translator = build_translator("Translator", [bot_publickey])
|
|
||||||
translator.run()
|
|
||||||
|
|
||||||
# Spawn DVM3 Kind 5100 Image Generation This one uses a specific backend called nova-server.
|
|
||||||
# If you want to use it, see the instructions in backends/nova_server
|
|
||||||
unstable_artist = build_unstable_diffusion("Unstable Diffusion", [bot_publickey])
|
|
||||||
unstable_artist.run()
|
|
||||||
|
|
||||||
# Spawn DVM4, another Instance of text-to-image, as before but use a different privatekey, model and lora this time.
|
|
||||||
sketcher = build_sketcher("Sketcher", [bot_publickey])
|
|
||||||
sketcher.run()
|
|
||||||
|
|
||||||
dalle = build_dalle("Dall-E 3", [bot_publickey])
|
|
||||||
dalle.run()
|
|
||||||
|
|
||||||
# We will run an optional bot that can communicate with the DVMs
|
# We will run an optional bot that can communicate with the DVMs
|
||||||
# Note this is very basic for now and still under development
|
# Note this is very basic for now and still under development
|
||||||
bot_config = DVMConfig()
|
bot_config = DVMConfig()
|
||||||
bot_config.PRIVATE_KEY = os.getenv("BOT_PRIVATE_KEY")
|
bot_config.PRIVATE_KEY = os.getenv("BOT_PRIVATE_KEY")
|
||||||
bot_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
|
bot_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
|
||||||
bot_config.LNBITS_URL = os.getenv("LNBITS_HOST")
|
bot_config.LNBITS_URL = os.getenv("LNBITS_HOST")
|
||||||
# Finally we add some of the DVMs we created before to the Bot and start it.
|
|
||||||
bot_config.SUPPORTED_DVMS = [sketcher, unstable_artist, dalle, translator]
|
|
||||||
|
|
||||||
bot = Bot
|
# Spawn some DVMs in the playground and run them
|
||||||
nostr_dvm_thread = Thread(target=bot, args=[bot_config])
|
# You can add arbitrary DVMs there and instantiate them here
|
||||||
nostr_dvm_thread.start()
|
|
||||||
|
# Spawn DVM1 Kind 5000: A local Text Extractor from PDFs
|
||||||
|
pdfextractor = build_pdf_extractor("PDF Extractor", [bot_publickey])
|
||||||
|
# If we don't add it to the bot, the bot will not provide access to the DVM
|
||||||
|
pdfextractor.run()
|
||||||
|
|
||||||
|
# Spawn DVM2 Kind 5002 Local Text Translation, calling the free Google API.
|
||||||
|
translator = build_translator("Translator", [bot_publickey])
|
||||||
|
bot_config.SUPPORTED_DVMS.append(translator) # We add translator to the bot
|
||||||
|
translator.run()
|
||||||
|
|
||||||
|
# Spawn DVM3 Kind 5100 Image Generation This one uses a specific backend called nova-server.
|
||||||
|
# If you want to use it, see the instructions in backends/nova_server
|
||||||
|
if os.getenv("NOVA_SERVER") is not None and os.getenv("NOVA_SERVER") != "":
|
||||||
|
unstable_artist = build_unstable_diffusion("Unstable Diffusion", [bot_publickey])
|
||||||
|
bot_config.SUPPORTED_DVMS.append(unstable_artist) # We add unstable Diffusion to the bot
|
||||||
|
unstable_artist.run()
|
||||||
|
|
||||||
|
# Spawn DVM4, another Instance of text-to-image, as before but use a different privatekey, model and lora this time.
|
||||||
|
if os.getenv("NOVA_SERVER") is not None and os.getenv("NOVA_SERVER") != "":
|
||||||
|
sketcher = build_sketcher("Sketcher", [bot_publickey])
|
||||||
|
bot_config.SUPPORTED_DVMS.append(sketcher) # We also add Sketcher to the bot
|
||||||
|
sketcher.run()
|
||||||
|
|
||||||
|
# Spawn DVM5, this one requires an OPENAI API Key and balance with OpenAI, you will move the task to them and pay
|
||||||
|
# per call. Make sure you have enough balance and the DVM's cost is set higher than what you pay yourself, except, you know,
|
||||||
|
# you're being generous.
|
||||||
|
if os.getenv("OPENAI_API_KEY") is not None and os.getenv("OPENAI_API_KEY") != "":
|
||||||
|
dalle = build_dalle("Dall-E 3", [bot_publickey])
|
||||||
|
bot_config.SUPPORTED_DVMS.append(dalle)
|
||||||
|
dalle.run()
|
||||||
|
|
||||||
|
bot = Bot(bot_config)
|
||||||
|
bot.run()
|
||||||
|
|
||||||
# Keep the main function alive for libraries like openai
|
# Keep the main function alive for libraries like openai
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from tasks.translation import Translation
|
|||||||
from utils.admin_utils import AdminConfig
|
from utils.admin_utils import AdminConfig
|
||||||
from utils.dvmconfig import DVMConfig
|
from utils.dvmconfig import DVMConfig
|
||||||
from utils.nip89_utils import NIP89Config
|
from utils.nip89_utils import NIP89Config
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This File is a playground to create DVMs. It shows some examples of DVMs that make use of the modules in the tasks folder
|
This File is a playground to create DVMs. It shows some examples of DVMs that make use of the modules in the tasks folder
|
||||||
These DVMs should be considered examples and will be extended in the future. env variables are used to not commit keys,
|
These DVMs should be considered examples and will be extended in the future. env variables are used to not commit keys,
|
||||||
@@ -32,6 +33,8 @@ task, for example an address or an API key.
|
|||||||
# their NIP89 announcement
|
# their NIP89 announcement
|
||||||
admin_config = AdminConfig()
|
admin_config = AdminConfig()
|
||||||
admin_config.REBROADCAST_NIP89 = False
|
admin_config.REBROADCAST_NIP89 = False
|
||||||
|
# Set rebroadcast to true once you have set your NIP89 descriptions and d tags. You only need to rebroadcast once you
|
||||||
|
# want to update your NIP89 descriptions
|
||||||
|
|
||||||
|
|
||||||
def build_pdf_extractor(name, dm_allowed_keys):
|
def build_pdf_extractor(name, dm_allowed_keys):
|
||||||
@@ -67,11 +70,11 @@ def build_translator(name, dm_allowed_keys):
|
|||||||
"language": {
|
"language": {
|
||||||
"required": False,
|
"required": False,
|
||||||
"values": ["en", "az", "be", "bg", "bn", "bs", "ca", "ceb", "co", "cs", "cy", "da", "de", "el", "eo", "es",
|
"values": ["en", "az", "be", "bg", "bn", "bs", "ca", "ceb", "co", "cs", "cy", "da", "de", "el", "eo", "es",
|
||||||
"et", "eu", "fa", "fi", "fr", "fy", "ga", "gd", "gl","gu", "ha", "haw", "hi", "hmn", "hr", "ht",
|
"et", "eu", "fa", "fi", "fr", "fy", "ga", "gd", "gl", "gu", "ha", "haw", "hi", "hmn", "hr", "ht",
|
||||||
"hu", "hy", "id", "ig", "is", "it", "he", "ja", "jv", "ka", "kk","km", "kn", "ko", "ku", "ky",
|
"hu", "hy", "id", "ig", "is", "it", "he", "ja", "jv", "ka", "kk", "km", "kn", "ko", "ku", "ky",
|
||||||
"la", "lb", "lo", "lt", "lv", "mg", "mi", "mk", "ml", "mn", "mr", "ms", "mt", "my", "ne", "nl",
|
"la", "lb", "lo", "lt", "lv", "mg", "mi", "mk", "ml", "mn", "mr", "ms", "mt", "my", "ne", "nl",
|
||||||
"no", "ny", "or", "pa", "pl", "ps", "pt", "ro", "ru", "sd", "si", "sk", "sl", "sm", "sn", "so",
|
"no", "ny", "or", "pa", "pl", "ps", "pt", "ro", "ru", "sd", "si", "sk", "sl", "sm", "sn", "so",
|
||||||
"sq", "sr", "st", "su", "sv", "sw", "ta", "te","tg", "th", "tl", "tr", "ug", "uk", "ur", "uz",
|
"sq", "sr", "st", "su", "sv", "sw", "ta", "te", "tg", "th", "tl", "tr", "ug", "uk", "ur", "uz",
|
||||||
"vi", "xh", "yi", "yo", "zh", "zu"]
|
"vi", "xh", "yi", "yo", "zh", "zu"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,12 +161,15 @@ def build_sketcher(name, dm_allowed_keys):
|
|||||||
return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config,
|
return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config,
|
||||||
admin_config=admin_config, options=options)
|
admin_config=admin_config, options=options)
|
||||||
|
|
||||||
|
|
||||||
def build_dalle(name, dm_allowed_keys):
|
def build_dalle(name, dm_allowed_keys):
|
||||||
dvm_config = DVMConfig()
|
dvm_config = DVMConfig()
|
||||||
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY3")
|
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY3")
|
||||||
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
|
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
|
||||||
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
|
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
|
||||||
dvm_config.DM_ALLOWED = dm_allowed_keys
|
dvm_config.DM_ALLOWED = dm_allowed_keys
|
||||||
|
profit_in_sats = 10
|
||||||
|
dvm_config.COST = int(((4.0 / (get_price_per_sat("USD") * 100)) + profit_in_sats))
|
||||||
|
|
||||||
nip90params = {
|
nip90params = {
|
||||||
"size": {
|
"size": {
|
||||||
@@ -186,4 +192,24 @@ def build_dalle(name, dm_allowed_keys):
|
|||||||
nip89config.CONTENT = json.dumps(nip89info)
|
nip89config.CONTENT = json.dumps(nip89info)
|
||||||
# We add an optional AdminConfig for this one, and tell the dvm to rebroadcast its NIP89
|
# We add an optional AdminConfig for this one, and tell the dvm to rebroadcast its NIP89
|
||||||
return ImageGenerationDALLE(name=name, dvm_config=dvm_config, nip89config=nip89config,
|
return ImageGenerationDALLE(name=name, dvm_config=dvm_config, nip89config=nip89config,
|
||||||
admin_config=admin_config)
|
admin_config=admin_config)
|
||||||
|
|
||||||
|
|
||||||
|
# Little Gimmick:
|
||||||
|
# For Dalle where we have to pay 4cent per image, we fetch current sat price in fiat
|
||||||
|
# and update cost at each start
|
||||||
|
def get_price_per_sat(currency):
|
||||||
|
import requests
|
||||||
|
|
||||||
|
url = "https://api.coinstats.app/public/v1/coins"
|
||||||
|
params = {"skip": 0, "limit": 1, "currency": currency}
|
||||||
|
try:
|
||||||
|
response = requests.get(url, params=params)
|
||||||
|
response_json = response.json()
|
||||||
|
|
||||||
|
bitcoin_price = response_json["coins"][0]["price"]
|
||||||
|
price_currency_per_sat = bitcoin_price / 100000000.0
|
||||||
|
except:
|
||||||
|
price_currency_per_sat = 0.0004
|
||||||
|
|
||||||
|
return price_currency_per_sat
|
||||||
|
|||||||
@@ -23,23 +23,14 @@ Params: -model # models: juggernaut, dynavision, colossusProject, newrea
|
|||||||
|
|
||||||
|
|
||||||
class ImageGenerationDALLE(DVMTaskInterface):
|
class ImageGenerationDALLE(DVMTaskInterface):
|
||||||
NAME: str = ""
|
|
||||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||||
TASK: str = "text-to-image"
|
TASK: str = "text-to-image"
|
||||||
COST: int = 120
|
COST: int = 120
|
||||||
PK: str
|
|
||||||
DVM = DVM
|
|
||||||
|
|
||||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, admin_config: AdminConfig = None,
|
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, admin_config: AdminConfig = None,
|
||||||
options=None):
|
options=None):
|
||||||
self.NAME = name
|
|
||||||
self.PK = dvm_config.PRIVATE_KEY
|
|
||||||
|
|
||||||
dvm_config.SUPPORTED_DVMS = [self]
|
self.init(name, dvm_config, admin_config, nip89config)
|
||||||
dvm_config.DB = "db/" + self.NAME + ".db"
|
|
||||||
dvm_config.NIP89 = self.NIP89_announcement(nip89config)
|
|
||||||
self.dvm_config = dvm_config
|
|
||||||
self.admin_config = admin_config
|
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
||||||
def is_input_supported(self, input_type, input_content):
|
def is_input_supported(self, input_type, input_content):
|
||||||
|
|||||||
@@ -20,22 +20,12 @@ Params: -model # models: juggernaut, dynavision, colossusProject, newrea
|
|||||||
|
|
||||||
|
|
||||||
class ImageGenerationSDXL(DVMTaskInterface):
|
class ImageGenerationSDXL(DVMTaskInterface):
|
||||||
NAME: str = ""
|
|
||||||
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE
|
||||||
TASK: str = "text-to-image"
|
TASK: str = "text-to-image"
|
||||||
COST: int = 50
|
COST: int = 50
|
||||||
PK: str
|
|
||||||
DVM = DVM
|
|
||||||
|
|
||||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, admin_config: AdminConfig = None, options=None):
|
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, admin_config: AdminConfig = None, options=None):
|
||||||
self.NAME = name
|
self.init(name, dvm_config, admin_config, nip89config)
|
||||||
self.PK = dvm_config.PRIVATE_KEY
|
|
||||||
|
|
||||||
dvm_config.SUPPORTED_DVMS = [self]
|
|
||||||
dvm_config.DB = "db/" + self.NAME + ".db"
|
|
||||||
dvm_config.NIP89 = self.NIP89_announcement(nip89config)
|
|
||||||
self.dvm_config = dvm_config
|
|
||||||
self.admin_config = admin_config
|
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
||||||
def is_input_supported(self, input_type, input_content):
|
def is_input_supported(self, input_type, input_content):
|
||||||
|
|||||||
@@ -21,22 +21,13 @@ Params: None
|
|||||||
|
|
||||||
|
|
||||||
class TextExtractionPDF(DVMTaskInterface):
|
class TextExtractionPDF(DVMTaskInterface):
|
||||||
NAME: str = ""
|
|
||||||
KIND: int = EventDefinitions.KIND_NIP90_EXTRACT_TEXT
|
KIND: int = EventDefinitions.KIND_NIP90_EXTRACT_TEXT
|
||||||
TASK: str = "pdf-to-text"
|
TASK: str = "pdf-to-text"
|
||||||
COST: int = 0
|
COST: int = 0
|
||||||
PK: str
|
|
||||||
DVM = DVM
|
|
||||||
|
|
||||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, admin_config: AdminConfig = None):
|
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, admin_config: AdminConfig = None, options=None):
|
||||||
self.NAME = name
|
self.init(name, dvm_config, admin_config, nip89config)
|
||||||
self.PK = dvm_config.PRIVATE_KEY
|
self.options = options
|
||||||
|
|
||||||
dvm_config.SUPPORTED_DVMS = [self]
|
|
||||||
dvm_config.DB = "db/" + self.NAME + ".db"
|
|
||||||
dvm_config.NIP89 = self.NIP89_announcement(nip89config)
|
|
||||||
self.dvm_config = dvm_config
|
|
||||||
self.admin_config = admin_config
|
|
||||||
|
|
||||||
|
|
||||||
def is_input_supported(self, input_type, input_content):
|
def is_input_supported(self, input_type, input_content):
|
||||||
|
|||||||
@@ -19,22 +19,14 @@ Params: -language The target language
|
|||||||
|
|
||||||
|
|
||||||
class Translation(DVMTaskInterface):
|
class Translation(DVMTaskInterface):
|
||||||
NAME: str = ""
|
|
||||||
KIND: int = EventDefinitions.KIND_NIP90_TRANSLATE_TEXT
|
KIND: int = EventDefinitions.KIND_NIP90_TRANSLATE_TEXT
|
||||||
TASK: str = "translation"
|
TASK: str = "translation"
|
||||||
COST: int = 0
|
COST: int = 0
|
||||||
PK: str
|
|
||||||
DVM = DVM
|
|
||||||
|
|
||||||
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, admin_config: AdminConfig = None):
|
def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, admin_config: AdminConfig = None,
|
||||||
self.NAME = name
|
options=None):
|
||||||
self.PK = dvm_config.PRIVATE_KEY
|
self.init(name, dvm_config, admin_config, nip89config)
|
||||||
|
self.options = options
|
||||||
dvm_config.SUPPORTED_DVMS = [self]
|
|
||||||
dvm_config.DB = "db/" + self.NAME + ".db"
|
|
||||||
dvm_config.NIP89 = self.NIP89_announcement(nip89config)
|
|
||||||
self.dvm_config = dvm_config
|
|
||||||
self.admin_config = admin_config
|
|
||||||
|
|
||||||
def is_input_supported(self, input_type, input_content):
|
def is_input_supported(self, input_type, input_content):
|
||||||
if input_type != "event" and input_type != "job" and input_type != "text":
|
if input_type != "event" and input_type != "job" and input_type != "text":
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ def admin_make_database_updates(adminconfig: AdminConfig = None, dvmconfig: DVMC
|
|||||||
|
|
||||||
|
|
||||||
if whitelistuser:
|
if whitelistuser:
|
||||||
user = get_or_add_user(db, publickey, client=client)
|
user = get_or_add_user(db, publickey, client=client, config=dvmconfig)
|
||||||
update_sql_table(db, user.npub, user.balance, True, False, user.nip05, user.lud16, user.name, user.lastactive)
|
update_sql_table(db, user.npub, user.balance, True, False, user.nip05, user.lud16, user.name, user.lastactive)
|
||||||
user = get_from_sql_table(db, publickey)
|
user = get_from_sql_table(db, publickey)
|
||||||
print(str(user.name) + " is whitelisted: " + str(user.iswhitelisted))
|
print(str(user.name) + " is whitelisted: " + str(user.iswhitelisted))
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ from dataclasses import dataclass
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from logging import Filter
|
from logging import Filter
|
||||||
|
|
||||||
from nostr_sdk import Timestamp, Keys, PublicKey, EventBuilder, Metadata, Filter, Options, Client
|
from nostr_sdk import Timestamp, Keys, PublicKey, EventBuilder, Filter, Client, Options
|
||||||
|
|
||||||
from utils.definitions import NEW_USER_BALANCE
|
|
||||||
from utils.nostr_utils import send_event
|
from utils.nostr_utils import send_event
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +64,7 @@ def add_to_sql_table(db, npub, sats, iswhitelisted, isblacklisted, nip05, lud16,
|
|||||||
con.commit()
|
con.commit()
|
||||||
con.close()
|
con.close()
|
||||||
except Error as e:
|
except Error as e:
|
||||||
print(e)
|
print("Error when Adding to DB: " + str(e))
|
||||||
|
|
||||||
|
|
||||||
def update_sql_table(db, npub, balance, iswhitelisted, isblacklisted, nip05, lud16, name, lastactive):
|
def update_sql_table(db, npub, balance, iswhitelisted, isblacklisted, nip05, lud16, name, lastactive):
|
||||||
@@ -87,7 +85,7 @@ def update_sql_table(db, npub, balance, iswhitelisted, isblacklisted, nip05, lud
|
|||||||
con.commit()
|
con.commit()
|
||||||
con.close()
|
con.close()
|
||||||
except Error as e:
|
except Error as e:
|
||||||
print(e)
|
print("Error Updating DB: " + str(e))
|
||||||
|
|
||||||
|
|
||||||
def get_from_sql_table(db, npub):
|
def get_from_sql_table(db, npub):
|
||||||
@@ -113,7 +111,7 @@ def get_from_sql_table(db, npub):
|
|||||||
return user
|
return user
|
||||||
|
|
||||||
except Error as e:
|
except Error as e:
|
||||||
print(e)
|
print("Error Getting from DB: " + str(e))
|
||||||
|
|
||||||
|
|
||||||
def delete_from_sql_table(db, npub):
|
def delete_from_sql_table(db, npub):
|
||||||
@@ -155,72 +153,70 @@ def list_db(db):
|
|||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
def update_user_balance(db, sender, sats, client, config):
|
def update_user_balance(db, npub, additional_sats, client, config):
|
||||||
user = get_from_sql_table(db, sender)
|
user = get_from_sql_table(db, npub)
|
||||||
if user is None:
|
if user is None:
|
||||||
add_to_sql_table(db, sender, (int(sats) + NEW_USER_BALANCE), False, False,
|
name, nip05, lud16 = fetch_user_metadata(npub, client)
|
||||||
"", "", "", Timestamp.now().as_secs())
|
add_to_sql_table(db, npub, (int(additional_sats) + config.NEW_USER_BALANCE), False, False,
|
||||||
print("NEW USER: " + sender + " Zap amount: " + str(sats) + " Sats.")
|
nip05, lud16, name, Timestamp.now().as_secs())
|
||||||
|
print("Adding User: " + npub + " (" + npub + ")")
|
||||||
else:
|
else:
|
||||||
user = get_from_sql_table(db, sender)
|
user = get_from_sql_table(db, npub)
|
||||||
print(str(sats))
|
new_balance = int(user.balance) + int(additional_sats)
|
||||||
|
update_sql_table(db, npub, new_balance, user.iswhitelisted, user.isblacklisted, user.nip05, user.lud16,
|
||||||
if user.nip05 is None:
|
|
||||||
user.nip05 = ""
|
|
||||||
if user.lud16 is None:
|
|
||||||
user.lud16 = ""
|
|
||||||
if user.name is None:
|
|
||||||
user.name = ""
|
|
||||||
|
|
||||||
new_balance = int(user.balance) + int(sats)
|
|
||||||
update_sql_table(db, sender, new_balance, user.iswhitelisted, user.isblacklisted, user.nip05, user.lud16,
|
|
||||||
user.name,
|
user.name,
|
||||||
Timestamp.now().as_secs())
|
Timestamp.now().as_secs())
|
||||||
print("UPDATE USER BALANCE: " + str(user.name) + " Zap amount: " + str(sats) + " Sats.")
|
print("Updated user balance for: " + str(user.name) +
|
||||||
|
" Zap amount: " + str(additional_sats) + " Sats. New balance: " + str(new_balance) +" Sats")
|
||||||
|
|
||||||
if config is not None:
|
if config is not None:
|
||||||
keys = Keys.from_sk_str(config.PRIVATE_KEY)
|
keys = Keys.from_sk_str(config.PRIVATE_KEY)
|
||||||
time.sleep(1.0)
|
time.sleep(1.0)
|
||||||
|
|
||||||
message = ("Added " + str(sats) + " Sats to balance. New balance is " + str(new_balance) + " Sats. ")
|
message = ("Added " + str(additional_sats) + " Sats to balance. New balance is " + str(new_balance) + " Sats.")
|
||||||
|
|
||||||
evt = EventBuilder.new_encrypted_direct_msg(keys, PublicKey.from_hex(sender), message,
|
evt = EventBuilder.new_encrypted_direct_msg(keys, PublicKey.from_hex(npub), message,
|
||||||
None).to_event(keys)
|
None).to_event(keys)
|
||||||
send_event(evt, client=client, dvm_config=config)
|
send_event(evt, client=client, dvm_config=config)
|
||||||
|
|
||||||
|
|
||||||
def get_or_add_user(db, npub, client):
|
def get_or_add_user(db, npub, client, config):
|
||||||
user = get_from_sql_table(db, npub)
|
user = get_from_sql_table(db, npub)
|
||||||
if user is None:
|
if user is None:
|
||||||
name, nip05, lud16 = fetch_user_metadata(npub, client)
|
try:
|
||||||
print("Adding User: " + npub + " (" + npub + ")")
|
name, nip05, lud16 = fetch_user_metadata(npub, client)
|
||||||
add_to_sql_table(db, npub, NEW_USER_BALANCE, False, False, nip05,
|
print("Adding User: " + npub + " (" + npub + ")")
|
||||||
lud16, name, Timestamp.now().as_secs())
|
add_to_sql_table(db, npub, config.NEW_USER_BALANCE, False, False, nip05,
|
||||||
user = get_from_sql_table(db, npub)
|
lud16, name, Timestamp.now().as_secs())
|
||||||
return user
|
user = get_from_sql_table(db, npub)
|
||||||
|
return user
|
||||||
|
except Exception as e:
|
||||||
|
print("Error Adding User to DB: " + str(e))
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
class DvmConfig:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def fetch_user_metadata(npub, client):
|
def fetch_user_metadata(npub, client):
|
||||||
name = ""
|
name = ""
|
||||||
nip05 = ""
|
nip05 = ""
|
||||||
lud16 = ""
|
lud16 = ""
|
||||||
|
|
||||||
# Get metadata
|
|
||||||
pk = PublicKey.from_hex(npub)
|
pk = PublicKey.from_hex(npub)
|
||||||
print(f"\nGetting profile metadata for {pk.to_bech32()}...")
|
print(f"\nGetting profile metadata for {pk.to_bech32()}...")
|
||||||
filter = Filter().kind(0).author(pk).limit(1)
|
profile_filter = Filter().kind(0).author(pk).limit(1)
|
||||||
events = client.get_events_of([filter], timedelta(seconds=3))
|
events = client.get_events_of([profile_filter], timedelta(seconds=5))
|
||||||
|
#TODO, it seems our client is still subscribed after that
|
||||||
|
|
||||||
if len(events) > 0:
|
if len(events) > 0:
|
||||||
latest_entry = events[0]
|
latest_entry = events[0]
|
||||||
newest = 0
|
latest_time = 0
|
||||||
for entry in events:
|
for entry in events:
|
||||||
if entry.created_at().as_secs() > newest:
|
if entry.created_at().as_secs() > latest_time:
|
||||||
newest = entry.created_at().as_secs()
|
latest_time = entry.created_at().as_secs()
|
||||||
latest_entry = entry
|
latest_entry = entry
|
||||||
|
|
||||||
print(latest_entry.content())
|
|
||||||
profile = json.loads(latest_entry.content())
|
profile = json.loads(latest_entry.content())
|
||||||
if profile.get("name"):
|
if profile.get("name"):
|
||||||
name = profile['name']
|
name = profile['name']
|
||||||
@@ -228,32 +224,4 @@ def fetch_user_metadata(npub, client):
|
|||||||
nip05 = profile['nip05']
|
nip05 = profile['nip05']
|
||||||
if profile.get("lud16"):
|
if profile.get("lud16"):
|
||||||
lud16 = profile['lud16']
|
lud16 = profile['lud16']
|
||||||
|
|
||||||
return name, nip05, lud16
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_user_metadata2(sender, client) -> (str, str, str):
|
|
||||||
name = ""
|
|
||||||
nip05 = ""
|
|
||||||
lud16 = ""
|
|
||||||
try:
|
|
||||||
pk = PublicKey.from_hex(sender)
|
|
||||||
print(f"\nGetting profile metadata for {pk.to_bech32()}...")
|
|
||||||
profile_filter = Filter().kind(0).author(pk).limit(1)
|
|
||||||
events = client.get_events_of([profile_filter], timedelta(seconds=1))
|
|
||||||
if len(events) > 0:
|
|
||||||
ev = events[0]
|
|
||||||
metadata = Metadata.from_json(ev.content())
|
|
||||||
name = metadata.get_display_name()
|
|
||||||
if str(name) == "" or name is None:
|
|
||||||
name = metadata.get_name()
|
|
||||||
nip05 = metadata.get_nip05()
|
|
||||||
lud16 = metadata.get_lud16()
|
|
||||||
else:
|
|
||||||
print("Profile not found")
|
|
||||||
return name, nip05, lud16
|
|
||||||
|
|
||||||
except:
|
|
||||||
print("Couldn't get meta information")
|
|
||||||
|
|
||||||
return name, nip05, lud16
|
return name, nip05, lud16
|
||||||
|
|||||||
@@ -2,10 +2,6 @@ import os
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from nostr_sdk import Event
|
from nostr_sdk import Event
|
||||||
|
|
||||||
NEW_USER_BALANCE: int = 250 # Free credits for new users
|
|
||||||
|
|
||||||
|
|
||||||
class EventDefinitions:
|
class EventDefinitions:
|
||||||
KIND_DM: int = 4
|
KIND_DM: int = 4
|
||||||
KIND_ZAP: int = 9735
|
KIND_ZAP: int = 9735
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from utils.nip89_utils import NIP89Announcement
|
|||||||
class DVMConfig:
|
class DVMConfig:
|
||||||
SUPPORTED_DVMS= []
|
SUPPORTED_DVMS= []
|
||||||
PRIVATE_KEY: str = os.getenv("NOSTR_PRIVATE_KEY")
|
PRIVATE_KEY: str = os.getenv("NOSTR_PRIVATE_KEY")
|
||||||
|
COST: int = None
|
||||||
|
|
||||||
RELAY_LIST = ["wss://relay.damus.io", "wss://nostr-pub.wellorder.net", "wss://nos.lol", "wss://nostr.wine",
|
RELAY_LIST = ["wss://relay.damus.io", "wss://nostr-pub.wellorder.net", "wss://nos.lol", "wss://nostr.wine",
|
||||||
"wss://relay.nostfiles.dev", "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg",
|
"wss://relay.nostfiles.dev", "wss://nostr.mom", "wss://nostr.oxtr.dev", "wss://relay.nostr.bg",
|
||||||
@@ -15,11 +16,11 @@ class DVMConfig:
|
|||||||
LNBITS_INVOICE_KEY = ''
|
LNBITS_INVOICE_KEY = ''
|
||||||
LNBITS_URL = 'https://lnbits.com'
|
LNBITS_URL = 'https://lnbits.com'
|
||||||
DB: str
|
DB: str
|
||||||
|
NEW_USER_BALANCE: int = 250 # Free credits for new users
|
||||||
NIP89: NIP89Announcement
|
NIP89: NIP89Announcement
|
||||||
DM_ALLOWED = []
|
DM_ALLOWED = []
|
||||||
|
|
||||||
REQUIRES_NIP05: bool = False
|
SHOW_RESULT_BEFORE_PAYMENT: bool = False # if this is true show results even when not paid right after autoprocess
|
||||||
SHOW_RESULT_BEFORE_PAYMENT: bool = True # if this is true show results even when not paid right after autoprocess
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import json
|
|||||||
import requests
|
import requests
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
from bech32 import bech32_decode, convertbits
|
from bech32 import bech32_decode, convertbits
|
||||||
from nostr_sdk import nostr_sdk, PublicKey, SecretKey
|
from nostr_sdk import nostr_sdk, PublicKey, SecretKey, Event
|
||||||
from utils.dvmconfig import DVMConfig
|
from utils.dvmconfig import DVMConfig
|
||||||
|
from utils.nostr_utils import get_event_by_id
|
||||||
|
|
||||||
|
|
||||||
def parse_amount_from_bolt11_invoice(bolt11_invoice: str) -> int:
|
def parse_amount_from_bolt11_invoice(bolt11_invoice: str) -> int:
|
||||||
@@ -35,6 +36,42 @@ def parse_amount_from_bolt11_invoice(bolt11_invoice: str) -> int:
|
|||||||
return int(number)
|
return int(number)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_zap_event_tags(zap_event, keys, name, client, config):
|
||||||
|
zapped_event = None
|
||||||
|
invoice_amount = 0
|
||||||
|
anon = False
|
||||||
|
sender = zap_event.pubkey()
|
||||||
|
|
||||||
|
for tag in zap_event.tags():
|
||||||
|
if tag.as_vec()[0] == 'bolt11':
|
||||||
|
invoice_amount = parse_amount_from_bolt11_invoice(tag.as_vec()[1])
|
||||||
|
elif tag.as_vec()[0] == 'e':
|
||||||
|
zapped_event = get_event_by_id(tag.as_vec()[1], client=client, config=config)
|
||||||
|
elif tag.as_vec()[0] == 'description':
|
||||||
|
zap_request_event = Event.from_json(tag.as_vec()[1])
|
||||||
|
sender = check_for_zapplepay(zap_request_event.pubkey().to_hex(),
|
||||||
|
zap_request_event.content())
|
||||||
|
for z_tag in zap_request_event.tags():
|
||||||
|
if z_tag.as_vec()[0] == 'anon':
|
||||||
|
if len(z_tag.as_vec()) > 1:
|
||||||
|
print("[" + name + "] Private Zap received.")
|
||||||
|
decrypted_content = decrypt_private_zap_message(z_tag.as_vec()[1],
|
||||||
|
keys.secret_key(),
|
||||||
|
zap_request_event.pubkey())
|
||||||
|
decrypted_private_event = Event.from_json(decrypted_content)
|
||||||
|
if decrypted_private_event.kind() == 9733:
|
||||||
|
sender = decrypted_private_event.pubkey().to_hex()
|
||||||
|
message = decrypted_private_event.content()
|
||||||
|
if message != "":
|
||||||
|
print("Zap Message: " + message)
|
||||||
|
else:
|
||||||
|
anon = True
|
||||||
|
print(
|
||||||
|
"[" + name + "] Anonymous Zap received. Unlucky, I don't know from whom, and never will")
|
||||||
|
|
||||||
|
return invoice_amount, zapped_event, sender, anon
|
||||||
|
|
||||||
|
|
||||||
def create_bolt11_ln_bits(sats: int, config: DVMConfig) -> (str, str):
|
def create_bolt11_ln_bits(sats: int, config: DVMConfig) -> (str, str):
|
||||||
url = config.LNBITS_URL + "/api/v1/payments"
|
url = config.LNBITS_URL + "/api/v1/payments"
|
||||||
data = {'out': False, 'amount': sats, 'memo': "Nostr-DVM " + config.NIP89.name}
|
data = {'out': False, 'amount': sats, 'memo': "Nostr-DVM " + config.NIP89.name}
|
||||||
|
|||||||
Reference in New Issue
Block a user