simplifiying code, adding a basic bot functionality

This commit is contained in:
Believethehype 2023-11-23 11:53:57 +01:00
parent dc727021f6
commit 8a7e3168f5
16 changed files with 434 additions and 156 deletions

View File

@ -1,15 +1,22 @@
NOSTR_PRIVATE_KEY = "nostrSecretkeyinhex"
NOSTR_TEST_CLIENT_PRIVATE_KEY = "nostrSecretkeyinhex_forthetestclient"
USER_DB_PATH = "nostrzaps.db"
NOSTR_PRIVATE_KEY = "a secret hexkey for some demo dvms"
NOSTR_PRIVATE_KEY2 = "another secret hexkey for demo dvm with another key"
BOT_PRIVATE_KEY = "The private key for a test bot that communicates with dvms"
NOSTR_TEST_CLIENT_PRIVATE_KEY = "a secret hex key for the test dvm client"
# Optional LNBITS options to create invoices (if empty, it will use the lud16 from profile)
# Optional LNBITS options to create invoices (if empty, it will use the lud16 from profile, make sure to set one)
LNBITS_INVOICE_KEY = ""
LNBITS_HOST = "https://lnbits.com"
TASK_TEXTEXTRACTION_NIP89_DTAG = "asdd"
# Some d tags we use in the testfile to announce or dvms. Create one at vendata.io)
TASK_TEXT_EXTRACTION_NIP89_DTAG = "asdd"
TASK_TRANSLATION_NIP89_DTAG = "abcded"
TASK_IMAGEGENERATION_NIP89_DTAG = "fgdfgdf"
TASK_IMAGEGENERATION_NIP89_DTAG2 = "fgdfgdf"
TASK_IMAGE_GENERATION_NIP89_DTAG = "fgdfgdf"
TASK_IMAGE_GENERATION_NIP89_DTAG2 = "fdgdfg"
#Backend Specific Options for tasks that require them
#nova-server is a local backend supporting some AI modules and needs to be installed separately,
#if dvms supporting it should be used
NOVA_SERVER = "127.0.0.1:37318"

View File

@ -62,7 +62,7 @@ def check_nova_server_status(jobID, address):
log = log_content[length:]
length = len(log_content)
if log != "":
print(log + " Status: " + str(status))
print(log)
# WAITING = 0, RUNNING = 1, FINISHED = 2, ERROR = 3
time.sleep(1.0)

160
bot.py Normal file
View File

@ -0,0 +1,160 @@
import json
import time
from threading import Thread
from nostr_sdk import Keys, Client, Timestamp, Filter, nip04_decrypt, HandleNotification, EventBuilder, PublicKey, Event
from utils.admin_utils import admin_make_database_updates
from utils.database_utils import get_or_add_user, update_user_balance
from utils.definitions import EventDefinitions
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
class Bot:
job_list: list
def __init__(self, dvm_config, admin_config=None):
self.dvm_config = dvm_config
self.admin_config = admin_config
self.keys = Keys.from_sk_str(dvm_config.PRIVATE_KEY)
self.client = Client(self.keys)
self.job_list = []
pk = self.keys.public_key()
self.dvm_config.DB = "db/bot.db"
print("Nostr BOT public key: " + str(pk.to_bech32()) + " Hex: " + str(pk.to_hex()) + " Supported DVM tasks: " +
', '.join(p.NAME + ":" + p.TASK for p in self.dvm_config.SUPPORTED_TASKS) + "\n")
for relay in self.dvm_config.RELAY_LIST:
self.client.add_relay(relay)
self.client.connect()
dm_zap_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM, EventDefinitions.KIND_ZAP]).since(
Timestamp.now())
self.client.subscribe([dm_zap_filter])
admin_make_database_updates(adminconfig=self.admin_config, dvmconfig=self.dvm_config, client=self.client)
class NotificationHandler(HandleNotification):
client = self.client
dvm_config = self.dvm_config
keys = self.keys
def handle(self, relay_url, nostr_event):
if EventDefinitions.KIND_DM:
print(
"[Bot] " + f"Received new DM from {relay_url}: {nostr_event.as_json()}")
handle_dm(nostr_event)
elif nostr_event.kind() == EventDefinitions.KIND_ZAP:
print("yay zap")
handle_zap(nostr_event)
def handle_msg(self, relay_url, msg):
return
def handle_dm(nostr_event):
sender = nostr_event.pubkey().to_hex()
try:
decrypted_text = nip04_decrypt(self.keys.secret_key(), nostr_event.pubkey(), nostr_event.content())
# TODO more advanced logic, more parsing, just very basic test functions for now
if decrypted_text[0].isdigit():
index = int(decrypted_text.split(' ')[0]) - 1
i_tag = decrypted_text.replace(decrypted_text.split(' ')[0] + " ", "")
keys = Keys.from_sk_str(self.dvm_config.SUPPORTED_TASKS[index].PK)
params = {
"sender": nostr_event.pubkey().to_hex(),
"input": i_tag,
"task": self.dvm_config.SUPPORTED_TASKS[index].TASK
}
message = json.dumps(params)
evt = EventBuilder.new_encrypted_direct_msg(self.keys, keys.public_key(),
message, None).to_event(self.keys)
send_event(evt, client=self.client, dvm_config=dvm_config)
elif decrypted_text.startswith('{"result":'):
dvm_result = json.loads(decrypted_text)
job_event = EventBuilder.new_encrypted_direct_msg(self.keys,
PublicKey.from_hex(dvm_result["sender"]),
dvm_result["result"],
None).to_event(self.keys)
send_event(job_event, client=self.client, dvm_config=dvm_config)
user = get_or_add_user(db=self.dvm_config.DB, npub=dvm_result["sender"], client=self.client)
print("BOT received and forwarded to " + user.name + ": " + str(decrypted_text))
else:
message = "DVMs that I support:\n\n"
index = 1
for p in self.dvm_config.SUPPORTED_TASKS:
message += str(index) + " " + p.NAME + " " + p.TASK + "\n"
index += 1
evt = EventBuilder.new_encrypted_direct_msg(self.keys, nostr_event.pubkey(),
message + "\nSelect an Index and provide an input ("
"e.g. 1 A purple ostrich)",
nostr_event.id()).to_event(self.keys)
send_event(evt, client=self.client, dvm_config=dvm_config)
except Exception as e:
print(e)
def handle_zap(zap_event):
zapped_event = None
invoice_amount = 0
anon = False
sender = zap_event.pubkey()
print("Zap received")
try:
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=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)
print(str(user.name))
if zapped_event is not None:
if not anon:
print("Note Zap received for Bot balance: " + str(invoice_amount) + " Sats from " + str(
user.name))
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
config=self.dvm_config)
# a regular note
elif not anon:
print("Profile Zap received for Bot balance: " + str(invoice_amount) + " Sats from " + str(
user.name))
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
config=self.dvm_config)
except Exception as e:
print(f"Error during content decryption: {e}")
self.client.handle_notifications(NotificationHandler())
while True:
time.sleep(1.0)

57
dvm.py
View File

@ -1,5 +1,7 @@
import json
from nostr_sdk import PublicKey, Keys, Client, Tag, Event, EventBuilder, Filter, HandleNotification, Timestamp, \
init_logger, LogLevel
init_logger, LogLevel, nip04_decrypt
import time
@ -39,21 +41,23 @@ class DVM:
pk = self.keys.public_key()
print("Nostr DVM public key: " + str(pk.to_bech32()) + "Hex: " + str(pk.to_hex()) + " Supported DVM tasks: " +
print("Nostr DVM public key: " + str(pk.to_bech32()) + " Hex: " + str(pk.to_hex()) + " Supported DVM tasks: " +
', '.join(p.NAME + ":" + p.TASK for p in self.dvm_config.SUPPORTED_TASKS) + "\n")
for relay in self.dvm_config.RELAY_LIST:
self.client.add_relay(relay)
self.client.connect()
dm_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())
#TODO only from allowed account
kinds = [EventDefinitions.KIND_NIP90_GENERIC]
for dvm in self.dvm_config.SUPPORTED_TASKS:
if dvm.KIND not in kinds:
kinds.append(dvm.KIND)
dvm_filter = (Filter().kinds(kinds).since(Timestamp.now()))
self.client.subscribe([dm_zap_filter, dvm_filter])
self.client.subscribe([dvm_filter, zap_filter, bot_dm_filter])
create_sql_table(self.dvm_config.DB)
admin_make_database_updates(adminconfig=self.admin_config, dvmconfig=self.dvm_config, client=self.client)
@ -69,6 +73,8 @@ class DVM:
handle_nip90_job_event(nostr_event)
elif nostr_event.kind() == EventDefinitions.KIND_ZAP:
handle_zap(nostr_event)
elif nostr_event.kind() == EventDefinitions.KIND_DM:
handle_dm(nostr_event)
def handle_msg(self, relay_url, msg):
return
@ -84,7 +90,7 @@ class DVM:
print("[" + self.dvm_config.NIP89.name + "] Request by blacklisted user, skipped")
elif task_supported:
print("[" + self.dvm_config.NIP89.name + "] Received new Task: " + task + " from " + user.name)
print("[" + self.dvm_config.NIP89.name + "] Received new Request: " + task + " from " + user.name)
amount = get_amount_per_task(task, self.dvm_config, duration)
if amount is None:
return
@ -218,6 +224,23 @@ class DVM:
except Exception as e:
print(f"Error during content decryption: {e}")
def handle_dm(dm_event):
decrypted_text = nip04_decrypt(self.keys.secret_key(), dm_event.pubkey(), dm_event.content())
ob = json.loads(decrypted_text)
#TODO SOME PARSING, OPTIONS, ZAP HANDLING
# One key might host multiple dvms, so we check current task
if ob['task'] == self.dvm_config.SUPPORTED_TASKS[0].TASK:
j_tag = Tag.parse(["j", self.dvm_config.SUPPORTED_TASKS[0].TASK])
i_tag = Tag.parse(["i", ob['input'], "text"])
tags = [j_tag, i_tag]
tags.append(Tag.parse(["y", dm_event.pubkey().to_hex()]))
tags.append(Tag.parse(["z", ob['sender']]))
job_event = EventBuilder(EventDefinitions.KIND_DM, "", tags).to_event(self.keys)
do_work(job_event, is_from_bot=True)
def check_event_has_not_unfinished_job_input(nevent, append, client, dvmconfig):
task_supported, task, duration = check_task_is_supported(nevent, client, False, config=dvmconfig)
if not task_supported:
@ -245,7 +268,7 @@ class DVM:
else:
return True
def check_and_return_event(data, original_event_str: str):
def check_and_return_event(data, original_event_str: str, is_from_bot: bool):
original_event = Event.from_json(original_event_str)
for x in self.job_list:
@ -271,7 +294,22 @@ class DVM:
try:
post_processed_content = post_process_result(data, original_event)
send_nostr_reply_event(post_processed_content, original_event_str)
if is_from_bot:
for tag in original_event.tags():
if tag.as_vec()[0] == "y": # TODO we temporally use internal tags to move information
receiver_key = PublicKey.from_hex(tag.as_vec()[1])
elif tag.as_vec()[0] == "z":
original_sender = tag.as_vec()[1]
params = {
"result": post_processed_content,
"sender": original_sender
}
message = json.dumps(params)
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)
else:
send_nostr_reply_event(post_processed_content, original_event_str)
except Exception as e:
respond_to_error(str(e), original_event_str, False)
@ -401,11 +439,11 @@ class DVM:
request_form = dvm.create_request_form_from_nostr_event(job_event, self.client,
self.dvm_config)
result = dvm.process(request_form)
check_and_return_event(result, str(job_event.as_json()))
check_and_return_event(result, str(job_event.as_json()), is_from_bot=is_from_bot)
except Exception as e:
print(e)
respond_to_error(str(e), job_event.as_json(), is_from_bot)
respond_to_error(str(e), job_event.as_json(), is_from_bot=is_from_bot)
return
self.client.handle_notifications(NotificationHandler())
@ -447,3 +485,4 @@ class DVM:
self.jobs_on_hold_list.remove(job)
time.sleep(1.0)

View File

@ -1,6 +1,10 @@
import json
from threading import Thread
from utils.admin_utils import AdminConfig
from utils.dvmconfig import DVMConfig
from utils.nip89_utils import NIP89Announcement
from dvm import DVM
class DVMTaskInterface:
@ -9,6 +13,9 @@ class DVMTaskInterface:
TASK: str
COST: int
PK: str
DVM = DVM
dvm_config: DVMConfig
admin_config: AdminConfig
def NIP89_announcement(self, d_tag, content):
nip89 = NIP89Announcement()
@ -19,6 +26,10 @@ class DVMTaskInterface:
nip89.content = content
return nip89
def run(self):
nostr_dvm_thread = Thread(target=self.DVM, args=[self.dvm_config, self.admin_config])
nostr_dvm_thread.start()
def is_input_supported(self, input_type, input_content) -> bool:
"""Check if input is supported for current Task."""
pass

109
main.py
View File

@ -3,98 +3,49 @@ from pathlib import Path
from threading import Thread
import dotenv
import utils.env as env
from tasks.imagegenerationsdxl import ImageGenerationSDXL
from tasks.textextractionpdf import TextExtractionPDF
from tasks.translation import Translation
from utils.admin_utils import AdminConfig
from nostr_sdk import Keys
from bot import Bot
from playground import build_pdf_extractor, build_translator, build_unstable_diffusion, build_sketcher
from utils.dvmconfig import DVMConfig
def run_nostr_dvm_with_local_config():
#Generate a optional Admin Config, in this case, whenever we give our DVMS this config, they will (re)broadcast
# their NIP89 announcement
admin_config = AdminConfig()
admin_config.REBROADCASTNIP89 = False
# 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().to_hex()
# Spawn the DVMs
# Add NIP89 events for each DVM
# Add the dtag here or in your .env file, so you can update your dvm later and change the content as needed.
# Get a dtag and the content at vendata.io
# 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
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv(env.NOSTR_PRIVATE_KEY)
dvm_config.LNBITS_INVOICE_KEY = os.getenv(env.LNBITS_INVOICE_KEY)
dvm_config.LNBITS_URL = os.getenv(env.LNBITS_HOST)
pdfextactor = TextExtractionPDF("PDF Extractor", dvm_config)
d_tag = os.getenv(env.TASK_TEXTEXTRACTION_NIP89_DTAG)
content = ("{\"name\":\"" + pdfextactor.NAME + "\","
"\"image\":\"https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg\","
"\"about\":\"I extract Text from pdf documents\","
"\"nip90Params\":{}}")
dvm_config.NIP89 = pdfextactor.NIP89_announcement(d_tag, content)
pdfextractor = build_pdf_extractor("PDF Extractor", [bot_publickey])
pdfextractor.run()
# Spawn DVM2 Kind 5002 Text Translation
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv(env.NOSTR_PRIVATE_KEY)
dvm_config.LNBITS_INVOICE_KEY = os.getenv(env.LNBITS_INVOICE_KEY)
dvm_config.LNBITS_URL = os.getenv(env.LNBITS_HOST)
translator = build_translator("Translator", [bot_publickey])
translator.run()
translator = Translation("Translator", dvm_config)
d_tag = os.getenv(env.TASK_TRANSLATION_NIP89_DTAG)
content = ("{\"name\":\"" + translator.NAME + "\","
"\"image\":\"https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg\","
"\"about\":\"I translate text from given text/event/job, ""currently using Google Translation Services into language defined in params.\","
"\"nip90Params\":{\"language\":{\"required\":true,"
"\"values\":[\"af\",\"am\",\"ar\",\"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\","
"\"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\",\"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\",\"vi\",\"xh\",\"yi\",\"yo\",\"zh\",\"zu\"]}}}")
# 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()
dvm_config.NIP89 = translator.NIP89_announcement(d_tag, content)
# 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
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv(env.NOSTR_PRIVATE_KEY)
dvm_config.LNBITS_INVOICE_KEY = os.getenv(env.LNBITS_INVOICE_KEY)
dvm_config.LNBITS_URL = os.getenv(env.LNBITS_HOST)
unstableartist = ImageGenerationSDXL("Unstable Diffusion", dvm_config, "unstable")
d_tag = os.getenv(env.TASK_IMAGEGENERATION_NIP89_DTAG)
content = ("{\"name\":\"" + unstableartist.NAME + "\","
"\"image\":\"https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg\","
"\"about\":\"I draw images based on a prompt with a Model called unstable diffusion.\","
"\"nip90Params\":{}}")
dvm_config.NIP89 = unstableartist.NIP89_announcement(d_tag, content)
# Spawn another Instance of text-to-image but use a different privatekey, model and lora this time.
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = "73b262d31edc6ea1316dffcc7daa772651d661e6475761b7b78291482c1bf5cb"
dvm_config.LNBITS_INVOICE_KEY = os.getenv(env.LNBITS_INVOICE_KEY)
dvm_config.LNBITS_URL = os.getenv(env.LNBITS_HOST)
#We add an optional AdminConfig for this one, and tell the dvm to rebroadcast its NIP89
sketcher = ImageGenerationSDXL("Sketcher", dvm_config, admin_config, default_model="mohawk", default_lora="timburton")
d_tag = os.getenv(env.TASK_IMAGEGENERATION_NIP89_DTAG2)
content = ("{\"name\":\"" + sketcher.NAME + "\","
"\"image\":\"https://image.nostr.build/229c14e440895da30de77b3ca145d66d4b04efb4027ba3c44ca147eecde891f1.jpg\","
"\"about\":\"I draw images based on a prompt with a Model called unstable diffusion.\","
"\"nip90Params\":{}}")
dvm_config.NIP89 = sketcher.NIP89_announcement(d_tag, content)
# 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()
# We will run an optional bot that can communicate with the DVMs
# Note this is very basic for now and still under development
bot_config = DVMConfig()
bot_config.PRIVATE_KEY = os.getenv("BOT_PRIVATE_KEY")
bot_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
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_TASKS = [sketcher, unstable_artist, translator]
bot = Bot
nostr_dvm_thread = Thread(target=bot, args=[bot_config])
nostr_dvm_thread.start()
if __name__ == '__main__':

129
playground.py Normal file
View File

@ -0,0 +1,129 @@
import json
import os
from tasks.imagegenerationsdxl import ImageGenerationSDXL
from tasks.textextractionpdf import TextExtractionPDF
from tasks.translation import Translation
from utils.admin_utils import AdminConfig
from utils.dvmconfig import DVMConfig
# Generate an optional Admin Config, in this case, whenever we give our DVMs this config, they will (re)broadcast
# their NIP89 announcement
admin_config = AdminConfig()
admin_config.REBROADCAST_NIP89 = False
def build_pdf_extractor(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.DM_ALLOWED = dm_allowed_keys
# Add NIP89
d_tag = os.getenv("TASK_TEXT_EXTRACTION_NIP89_DTAG")
nip90params = {}
nip89info = {
"name": name,
"image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg",
"about": "I extract text from pdf documents",
"nip90Params": nip90params
}
return TextExtractionPDF(name=name, dvm_config=dvm_config, nip89d_tag=d_tag, nip89info=json.dumps(nip89info),
admin_config=admin_config)
def build_translator(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.DM_ALLOWED = dm_allowed_keys
d_tag = os.getenv("TASK_TRANSLATION_NIP89_DTAG")
nip90params = {
"language": {
"required": False,
"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",
"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",
"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",
"vi", "xh", "yi", "yo", "zh", "zu"]
}
}
nip89info = {
"name": name,
"image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg",
"about": "I translate text from given text/event/job. Currently using Google Translation Services to translate "
"input into the language defined in params.",
"nip90Params": nip90params
}
return Translation(name=name, dvm_config=dvm_config, nip89d_tag=d_tag, nip89info=json.dumps(nip89info),
admin_config=admin_config)
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.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
# address it should use. These parameters can be freely defined in the task component
options = {'default_model': "unstable", 'nova_server': os.getenv("NOVA_SERVER")}
d_tag = os.getenv("TASK_IMAGE_GENERATION_NIP89_DTAG")
nip90params = {
"negative_prompt": {
"required": False,
"values": []
},
"ratio": {
"required": False,
"values": ["1:1", "4:3", "16:9", "3:4", "9:16", "10:16"]
}
}
nip89info = {
"name": name,
"image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg",
"about": "I draw images based on a prompt with a Model called unstable diffusion",
"nip90Params": nip90params
}
return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89d_tag=d_tag, nip89info=json.dumps(nip89info),
admin_config=admin_config, options=options)
def build_sketcher(name, dm_allowed_keys):
dvm_config = DVMConfig()
dvm_config.PRIVATE_KEY = os.getenv("NOSTR_PRIVATE_KEY2")
dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY")
dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST")
dvm_config.DM_ALLOWED = dm_allowed_keys
d_tag = os.getenv("TASK_IMAGE_GENERATION_NIP89_DTAG2")
nip90params = {
"negative_prompt": {
"required": False,
"values": []
},
"ratio": {
"required": False,
"values": ["1:1", "4:3", "16:9", "3:4", "9:16", "10:16"]
}
}
nip89info = {
"name": name,
"image": "https://image.nostr.build/229c14e440895da30de77b3ca145d66d4b04efb4027ba3c44ca147eecde891f1.jpg",
"about": "I draw images based on a prompt in the style of paper sketches",
"nip90Params": nip90params
}
# A module might have options it can be initialized with, here we set a default model, lora and the nova-server
# address it should use. These parameters can be freely defined in the task component
options = {'default_model': "mohawk", 'default_lora': "timburton", 'nova_server': os.getenv("NOVA_SERVER")}
# We add an optional AdminConfig for this one, and tell the dvm to rebroadcast its NIP89
return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89d_tag=d_tag, nip89info=json.dumps(nip89info),
admin_config=admin_config, options=options)

View File

@ -26,19 +26,18 @@ class ImageGenerationSDXL(DVMTaskInterface):
TASK: str = "text-to-image"
COST: int = 50
PK: str
DVM = DVM
def __init__(self, name, dvm_config: DVMConfig, admin_config: AdminConfig = None, default_model=None,
default_lora=None):
def __init__(self, name, dvm_config: DVMConfig, nip89d_tag: str, nip89info: str, admin_config: AdminConfig = None, options=None):
self.NAME = name
self.PK = dvm_config.PRIVATE_KEY
dvm_config.SUPPORTED_TASKS = [self]
dvm_config.DB = "db/" + self.NAME + ".db"
self.PK = dvm_config.PRIVATE_KEY
self.default_model = default_model
self.default_lora = default_lora
dvm = DVM
nostr_dvm_thread = Thread(target=dvm, args=[dvm_config, admin_config])
nostr_dvm_thread.start()
dvm_config.NIP89 = self.NIP89_announcement(nip89d_tag, nip89info)
self.dvm_config = dvm_config
self.admin_config = admin_config
self.options = options
def is_input_supported(self, input_type, input_content):
if input_type != "text":
@ -51,19 +50,19 @@ class ImageGenerationSDXL(DVMTaskInterface):
prompt = ""
negative_prompt = ""
if self.default_model is None:
model = "stabilityai/stable-diffusion-xl-base-1.0"
if self.options.get("default_model"):
model = self.options['default_model']
else:
model = self.default_model
model = "stabilityai/stable-diffusion-xl-base-1.0"
ratio_width = "1"
ratio_height = "1"
width = ""
height = ""
if self.default_lora == None:
lora = ""
if self.options.get("default_lora"):
lora = self.options['default_lora']
else:
lora = self.default_lora
lora = ""
lora_weight = ""
strength = ""
guidance_scale = ""
@ -149,12 +148,12 @@ class ImageGenerationSDXL(DVMTaskInterface):
def process(self, request_form):
try:
# Call the process route of NOVA-Server with our request form.
response = send_request_to_nova_server(request_form, os.environ["NOVA_SERVER"])
response = send_request_to_nova_server(request_form, self.options['nova_server'])
if bool(json.loads(response)['success']):
print("Job " + request_form['jobID'] + " sent to nova-server")
pool = ThreadPool(processes=1)
thread = pool.apply_async(check_nova_server_status, (request_form['jobID'], os.environ["NOVA_SERVER"]))
thread = pool.apply_async(check_nova_server_status, (request_form['jobID'], self.options['nova_server']))
print("Wait for results of NOVA-Server...")
result = thread.get()
return str(result)

View File

@ -25,16 +25,18 @@ class TextExtractionPDF(DVMTaskInterface):
TASK: str = "pdf-to-text"
COST: int = 0
PK: str
DVM = DVM
def __init__(self, name, dvm_config: DVMConfig, admin_config: AdminConfig = None):
def __init__(self, name, dvm_config: DVMConfig, nip89d_tag: str, nip89info: str, admin_config: AdminConfig = None):
self.NAME = name
dvm_config.SUPPORTED_TASKS = [self]
dvm_config.DB = "db/" + self.NAME + ".db"
self.PK = dvm_config.PRIVATE_KEY
dvm = DVM
nostr_dvm_thread = Thread(target=dvm, args=[dvm_config, admin_config])
nostr_dvm_thread.start()
dvm_config.SUPPORTED_TASKS = [self]
dvm_config.DB = "db/" + self.NAME + ".db"
dvm_config.NIP89 = self.NIP89_announcement(nip89d_tag, nip89info)
self.dvm_config = dvm_config
self.admin_config = admin_config
def is_input_supported(self, input_type, input_content):
if input_type != "url" and input_type != "event":

View File

@ -23,16 +23,18 @@ class Translation(DVMTaskInterface):
TASK: str = "translation"
COST: int = 0
PK: str
DVM = DVM
def __init__(self, name, dvm_config: DVMConfig, admin_config: AdminConfig = None):
def __init__(self, name, dvm_config: DVMConfig, nip89d_tag: str, nip89info: str, admin_config: AdminConfig = None):
self.NAME = name
dvm_config.SUPPORTED_TASKS = [self]
dvm_config.DB = "db/" + self.NAME + ".db"
self.PK = dvm_config.PRIVATE_KEY
dvm = DVM
nostr_dvm_thread = Thread(target=dvm, args=[dvm_config, admin_config])
nostr_dvm_thread.start()
dvm_config.SUPPORTED_TASKS = [self]
dvm_config.DB = "db/" + self.NAME + ".db"
dvm_config.NIP89 = self.NIP89_announcement(nip89d_tag, nip89info)
self.dvm_config = dvm_config
self.admin_config = admin_config
def is_input_supported(self, input_type, input_content):
if input_type != "event" and input_type != "job" and input_type != "text":

View File

@ -11,12 +11,10 @@ from utils.dvmconfig import DVMConfig
from utils.nostr_utils import send_event
from utils.definitions import EventDefinitions
import utils.env as env
# TODO HINT: Best use this path with a previously whitelisted privkey, as zapping events is not implemented in the lib/code
def nostr_client_test_translation(input, kind, lang, sats, satsmax):
keys = Keys.from_sk_str(os.getenv(env.NOSTR_TEST_CLIENT_PRIVATE_KEY))
keys = Keys.from_sk_str(os.getenv("NOSTR_TEST_CLIENT_PRIVATE_KEY"))
if kind == "text":
iTag = Tag.parse(["i", input, "text"])
elif kind == "event":
@ -43,7 +41,7 @@ def nostr_client_test_translation(input, kind, lang, sats, satsmax):
def nostr_client_test_image(prompt):
keys = Keys.from_sk_str(os.getenv(env.NOSTR_TEST_CLIENT_PRIVATE_KEY))
keys = Keys.from_sk_str(os.getenv("NOSTR_TEST_CLIENT_PRIVATE_KEY"))
iTag = Tag.parse(["i", prompt, "text"])
outTag = Tag.parse(["output", "image/png;format=url"])
@ -69,7 +67,7 @@ def nostr_client_test_image(prompt):
return event.as_json()
def nostr_client():
keys = Keys.from_sk_str(os.getenv(env.NOSTR_TEST_CLIENT_PRIVATE_KEY))
keys = Keys.from_sk_str(os.getenv("NOSTR_TEST_CLIENT_PRIVATE_KEY"))
sk = keys.secret_key()
pk = keys.public_key()
print(f"Nostr Client public key: {pk.to_bech32()}, Hex: {pk.to_hex()} ")

View File

@ -10,7 +10,7 @@ from utils.nip89_utils import nip89_announce_tasks
from utils.nostr_utils import send_event
class AdminConfig:
REBROADCASTNIP89: bool = False
REBROADCAST_NIP89: bool = False
WHITELISTUSER: bool = False
UNWHITELISTUSER: bool = False
BLACKLISTUSER: bool = False
@ -34,7 +34,7 @@ def admin_make_database_updates(adminconfig: AdminConfig = None, dvmconfig: DVMC
db = dvmconfig.DB
rebroadcast_nip89 = adminconfig.REBROADCASTNIP89
rebroadcast_nip89 = adminconfig.REBROADCAST_NIP89
cleandb = adminconfig.ClEANDB
listdatabase = adminconfig.LISTDATABASE
deleteuser = adminconfig.DELETEUSER

View File

@ -220,8 +220,8 @@ def fetch_user_metadata(sender, client) -> (str, str, str):
name = metadata.get_display_name()
if str(name) == "" or name is None:
name = metadata.get_name()
nip05 = metadata.get_nip05()
lud16 = metadata.get_lud16()
nip05 = metadata.get_nip05()
lud16 = metadata.get_lud16()
except:
print("Couldn't get meta information")

View File

@ -3,13 +3,9 @@ from dataclasses import dataclass
from nostr_sdk import Event
from utils import env
NEW_USER_BALANCE: int = 250 # Free credits for new users
class EventDefinitions:
KIND_DM: int = 4
KIND_ZAP: int = 9735
@ -41,8 +37,6 @@ class EventDefinitions:
KIND_NIP90_RESULT_GENERIC]
@dataclass
class JobToWatch:
event_id: str

View File

@ -1,12 +1,11 @@
import os
from utils import env
from utils.nip89_utils import NIP89Announcement
class DVMConfig:
SUPPORTED_TASKS = []
PRIVATE_KEY: str = os.getenv(env.NOSTR_PRIVATE_KEY)
PRIVATE_KEY: str = os.getenv("NOSTR_PRIVATE_KEY")
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",
@ -17,6 +16,7 @@ class DVMConfig:
LNBITS_URL = 'https://lnbits.com'
DB: str
NIP89: NIP89Announcement
DM_ALLOWED = []
REQUIRES_NIP05: bool = False
SHOW_RESULT_BEFORE_PAYMENT: bool = True # if this is true show results even when not paid right after autoprocess

View File

@ -1,14 +0,0 @@
NOSTR_PRIVATE_KEY = "NOSTR_PRIVATE_KEY"
NOSTR_TEST_CLIENT_PRIVATE_KEY = "NOSTR_TEST_CLIENT_PRIVATE_KEY"
USER_DB_PATH = "USER_DB_PATH"
LNBITS_INVOICE_KEY = "LNBITS_INVOICE_KEY"
LNBITS_HOST = "LNBITS_HOST"
TASK_TRANSLATION_NIP89_DTAG = "TASK_TRANSLATION_NIP89_DTAG"
TASK_TEXTEXTRACTION_NIP89_DTAG = "TASK_TEXTEXTRACTION_NIP89_DTAG"
TASK_IMAGEGENERATION_NIP89_DTAG = "TASK_IMAGEGENERATION_NIP89_DTAG"
TASK_IMAGEGENERATION_NIP89_DTAG2 = "TASK_IMAGEGENERATION_NIP89_DTAG2"